pax_global_header00006660000000000000000000000064125505151260014514gustar00rootroot0000000000000052 comment=3c3b356cb5e387887499beb2eedddce185f36944 mosh-1.2.5/000077500000000000000000000000001255051512600124675ustar00rootroot00000000000000mosh-1.2.5/.gitignore000066400000000000000000000004551255051512600144630ustar00rootroot00000000000000*.a *.o *.pb.cc *.pb.h .deps Makefile Makefile.in .cproject .project /INSTALL /aclocal.m4 /ar-lib /autom4te.cache /compile /config.h /config.h.in /config.h.in~ /config.log /config.status /configure /depcomp /install-sh /missing /mosh-*.tar.gz /stamp-h1 /test-driver /VERSION /scripts/mosh /version.h mosh-1.2.5/AUTHORS000066400000000000000000000001561255051512600135410ustar00rootroot00000000000000Keith Winstein Anders Kaseorg Quentin Smith Richard Tibbetts Keegan McAllister John Hood mosh-1.2.5/COPYING000066400000000000000000001045131255051512600135260ustar00rootroot00000000000000 GNU GENERAL PUBLIC LICENSE Version 3, 29 June 2007 Copyright (C) 2007 Free Software Foundation, Inc. Everyone is permitted to copy and distribute verbatim copies of this license document, but changing it is not allowed. Preamble The GNU General Public License is a free, copyleft license for software and other kinds of works. The licenses for most software and other practical works are designed to take away your freedom to share and change the works. By contrast, the GNU General Public License is intended to guarantee your freedom to share and change all versions of a program--to make sure it remains free software for all its users. We, the Free Software Foundation, use the GNU General Public License for most of our software; it applies also to any other work released this way by its authors. You can apply it to your programs, too. When we speak of free software, we are referring to freedom, not price. Our General Public Licenses are designed to make sure that you have the freedom to distribute copies of free software (and charge for them if you wish), that you receive source code or can get it if you want it, that you can change the software or use pieces of it in new free programs, and that you know you can do these things. To protect your rights, we need to prevent others from denying you these rights or asking you to surrender the rights. Therefore, you have certain responsibilities if you distribute copies of the software, or if you modify it: responsibilities to respect the freedom of others. For example, if you distribute copies of such a program, whether gratis or for a fee, you must pass on to the recipients the same freedoms that you received. You must make sure that they, too, receive or can get the source code. And you must show them these terms so they know their rights. Developers that use the GNU GPL protect your rights with two steps: (1) assert copyright on the software, and (2) offer you this License giving you legal permission to copy, distribute and/or modify it. For the developers' and authors' protection, the GPL clearly explains that there is no warranty for this free software. For both users' and authors' sake, the GPL requires that modified versions be marked as changed, so that their problems will not be attributed erroneously to authors of previous versions. Some devices are designed to deny users access to install or run modified versions of the software inside them, although the manufacturer can do so. This is fundamentally incompatible with the aim of protecting users' freedom to change the software. The systematic pattern of such abuse occurs in the area of products for individuals to use, which is precisely where it is most unacceptable. Therefore, we have designed this version of the GPL to prohibit the practice for those products. If such problems arise substantially in other domains, we stand ready to extend this provision to those domains in future versions of the GPL, as needed to protect the freedom of users. Finally, every program is threatened constantly by software patents. States should not allow patents to restrict development and use of software on general-purpose computers, but in those that do, we wish to avoid the special danger that patents applied to a free program could make it effectively proprietary. To prevent this, the GPL assures that patents cannot be used to render the program non-free. The precise terms and conditions for copying, distribution and modification follow. TERMS AND CONDITIONS 0. Definitions. "This License" refers to version 3 of the GNU General Public License. "Copyright" also means copyright-like laws that apply to other kinds of works, such as semiconductor masks. "The Program" refers to any copyrightable work licensed under this License. Each licensee is addressed as "you". "Licensees" and "recipients" may be individuals or organizations. To "modify" a work means to copy from or adapt all or part of the work in a fashion requiring copyright permission, other than the making of an exact copy. The resulting work is called a "modified version" of the earlier work or a work "based on" the earlier work. A "covered work" means either the unmodified Program or a work based on the Program. To "propagate" a work means to do anything with it that, without permission, would make you directly or secondarily liable for infringement under applicable copyright law, except executing it on a computer or modifying a private copy. Propagation includes copying, distribution (with or without modification), making available to the public, and in some countries other activities as well. To "convey" a work means any kind of propagation that enables other parties to make or receive copies. Mere interaction with a user through a computer network, with no transfer of a copy, is not conveying. An interactive user interface displays "Appropriate Legal Notices" to the extent that it includes a convenient and prominently visible feature that (1) displays an appropriate copyright notice, and (2) tells the user that there is no warranty for the work (except to the extent that warranties are provided), that licensees may convey the work under this License, and how to view a copy of this License. If the interface presents a list of user commands or options, such as a menu, a prominent item in the list meets this criterion. 1. Source Code. The "source code" for a work means the preferred form of the work for making modifications to it. "Object code" means any non-source form of a work. A "Standard Interface" means an interface that either is an official standard defined by a recognized standards body, or, in the case of interfaces specified for a particular programming language, one that is widely used among developers working in that language. The "System Libraries" of an executable work include anything, other than the work as a whole, that (a) is included in the normal form of packaging a Major Component, but which is not part of that Major Component, and (b) serves only to enable use of the work with that Major Component, or to implement a Standard Interface for which an implementation is available to the public in source code form. A "Major Component", in this context, means a major essential component (kernel, window system, and so on) of the specific operating system (if any) on which the executable work runs, or a compiler used to produce the work, or an object code interpreter used to run it. The "Corresponding Source" for a work in object code form means all the source code needed to generate, install, and (for an executable work) run the object code and to modify the work, including scripts to control those activities. However, it does not include the work's System Libraries, or general-purpose tools or generally available free programs which are used unmodified in performing those activities but which are not part of the work. For example, Corresponding Source includes interface definition files associated with source files for the work, and the source code for shared libraries and dynamically linked subprograms that the work is specifically designed to require, such as by intimate data communication or control flow between those subprograms and other parts of the work. The Corresponding Source need not include anything that users can regenerate automatically from other parts of the Corresponding Source. The Corresponding Source for a work in source code form is that same work. 2. Basic Permissions. All rights granted under this License are granted for the term of copyright on the Program, and are irrevocable provided the stated conditions are met. This License explicitly affirms your unlimited permission to run the unmodified Program. The output from running a covered work is covered by this License only if the output, given its content, constitutes a covered work. This License acknowledges your rights of fair use or other equivalent, as provided by copyright law. You may make, run and propagate covered works that you do not convey, without conditions so long as your license otherwise remains in force. You may convey covered works to others for the sole purpose of having them make modifications exclusively for you, or provide you with facilities for running those works, provided that you comply with the terms of this License in conveying all material for which you do not control copyright. Those thus making or running the covered works for you must do so exclusively on your behalf, under your direction and control, on terms that prohibit them from making any copies of your copyrighted material outside their relationship with you. Conveying under any other circumstances is permitted solely under the conditions stated below. Sublicensing is not allowed; section 10 makes it unnecessary. 3. Protecting Users' Legal Rights From Anti-Circumvention Law. No covered work shall be deemed part of an effective technological measure under any applicable law fulfilling obligations under article 11 of the WIPO copyright treaty adopted on 20 December 1996, or similar laws prohibiting or restricting circumvention of such measures. When you convey a covered work, you waive any legal power to forbid circumvention of technological measures to the extent such circumvention is effected by exercising rights under this License with respect to the covered work, and you disclaim any intention to limit operation or modification of the work as a means of enforcing, against the work's users, your or third parties' legal rights to forbid circumvention of technological measures. 4. Conveying Verbatim Copies. You may convey verbatim copies of the Program's source code as you receive it, in any medium, provided that you conspicuously and appropriately publish on each copy an appropriate copyright notice; keep intact all notices stating that this License and any non-permissive terms added in accord with section 7 apply to the code; keep intact all notices of the absence of any warranty; and give all recipients a copy of this License along with the Program. You may charge any price or no price for each copy that you convey, and you may offer support or warranty protection for a fee. 5. Conveying Modified Source Versions. You may convey a work based on the Program, or the modifications to produce it from the Program, in the form of source code under the terms of section 4, provided that you also meet all of these conditions: a) The work must carry prominent notices stating that you modified it, and giving a relevant date. b) The work must carry prominent notices stating that it is released under this License and any conditions added under section 7. This requirement modifies the requirement in section 4 to "keep intact all notices". c) You must license the entire work, as a whole, under this License to anyone who comes into possession of a copy. This License will therefore apply, along with any applicable section 7 additional terms, to the whole of the work, and all its parts, regardless of how they are packaged. This License gives no permission to license the work in any other way, but it does not invalidate such permission if you have separately received it. d) If the work has interactive user interfaces, each must display Appropriate Legal Notices; however, if the Program has interactive interfaces that do not display Appropriate Legal Notices, your work need not make them do so. A compilation of a covered work with other separate and independent works, which are not by their nature extensions of the covered work, and which are not combined with it such as to form a larger program, in or on a volume of a storage or distribution medium, is called an "aggregate" if the compilation and its resulting copyright are not used to limit the access or legal rights of the compilation's users beyond what the individual works permit. Inclusion of a covered work in an aggregate does not cause this License to apply to the other parts of the aggregate. 6. Conveying Non-Source Forms. You may convey a covered work in object code form under the terms of sections 4 and 5, provided that you also convey the machine-readable Corresponding Source under the terms of this License, in one of these ways: a) Convey the object code in, or embodied in, a physical product (including a physical distribution medium), accompanied by the Corresponding Source fixed on a durable physical medium customarily used for software interchange. b) Convey the object code in, or embodied in, a physical product (including a physical distribution medium), accompanied by a written offer, valid for at least three years and valid for as long as you offer spare parts or customer support for that product model, to give anyone who possesses the object code either (1) a copy of the Corresponding Source for all the software in the product that is covered by this License, on a durable physical medium customarily used for software interchange, for a price no more than your reasonable cost of physically performing this conveying of source, or (2) access to copy the Corresponding Source from a network server at no charge. c) Convey individual copies of the object code with a copy of the written offer to provide the Corresponding Source. This alternative is allowed only occasionally and noncommercially, and only if you received the object code with such an offer, in accord with subsection 6b. d) Convey the object code by offering access from a designated place (gratis or for a charge), and offer equivalent access to the Corresponding Source in the same way through the same place at no further charge. You need not require recipients to copy the Corresponding Source along with the object code. If the place to copy the object code is a network server, the Corresponding Source may be on a different server (operated by you or a third party) that supports equivalent copying facilities, provided you maintain clear directions next to the object code saying where to find the Corresponding Source. Regardless of what server hosts the Corresponding Source, you remain obligated to ensure that it is available for as long as needed to satisfy these requirements. e) Convey the object code using peer-to-peer transmission, provided you inform other peers where the object code and Corresponding Source of the work are being offered to the general public at no charge under subsection 6d. A separable portion of the object code, whose source code is excluded from the Corresponding Source as a System Library, need not be included in conveying the object code work. A "User Product" is either (1) a "consumer product", which means any tangible personal property which is normally used for personal, family, or household purposes, or (2) anything designed or sold for incorporation into a dwelling. In determining whether a product is a consumer product, doubtful cases shall be resolved in favor of coverage. For a particular product received by a particular user, "normally used" refers to a typical or common use of that class of product, regardless of the status of the particular user or of the way in which the particular user actually uses, or expects or is expected to use, the product. A product is a consumer product regardless of whether the product has substantial commercial, industrial or non-consumer uses, unless such uses represent the only significant mode of use of the product. "Installation Information" for a User Product means any methods, procedures, authorization keys, or other information required to install and execute modified versions of a covered work in that User Product from a modified version of its Corresponding Source. The information must suffice to ensure that the continued functioning of the modified object code is in no case prevented or interfered with solely because modification has been made. If you convey an object code work under this section in, or with, or specifically for use in, a User Product, and the conveying occurs as part of a transaction in which the right of possession and use of the User Product is transferred to the recipient in perpetuity or for a fixed term (regardless of how the transaction is characterized), the Corresponding Source conveyed under this section must be accompanied by the Installation Information. But this requirement does not apply if neither you nor any third party retains the ability to install modified object code on the User Product (for example, the work has been installed in ROM). The requirement to provide Installation Information does not include a requirement to continue to provide support service, warranty, or updates for a work that has been modified or installed by the recipient, or for the User Product in which it has been modified or installed. Access to a network may be denied when the modification itself materially and adversely affects the operation of the network or violates the rules and protocols for communication across the network. Corresponding Source conveyed, and Installation Information provided, in accord with this section must be in a format that is publicly documented (and with an implementation available to the public in source code form), and must require no special password or key for unpacking, reading or copying. 7. Additional Terms. "Additional permissions" are terms that supplement the terms of this License by making exceptions from one or more of its conditions. Additional permissions that are applicable to the entire Program shall be treated as though they were included in this License, to the extent that they are valid under applicable law. If additional permissions apply only to part of the Program, that part may be used separately under those permissions, but the entire Program remains governed by this License without regard to the additional permissions. When you convey a copy of a covered work, you may at your option remove any additional permissions from that copy, or from any part of it. (Additional permissions may be written to require their own removal in certain cases when you modify the work.) You may place additional permissions on material, added by you to a covered work, for which you have or can give appropriate copyright permission. Notwithstanding any other provision of this License, for material you add to a covered work, you may (if authorized by the copyright holders of that material) supplement the terms of this License with terms: a) Disclaiming warranty or limiting liability differently from the terms of sections 15 and 16 of this License; or b) Requiring preservation of specified reasonable legal notices or author attributions in that material or in the Appropriate Legal Notices displayed by works containing it; or c) Prohibiting misrepresentation of the origin of that material, or requiring that modified versions of such material be marked in reasonable ways as different from the original version; or d) Limiting the use for publicity purposes of names of licensors or authors of the material; or e) Declining to grant rights under trademark law for use of some trade names, trademarks, or service marks; or f) Requiring indemnification of licensors and authors of that material by anyone who conveys the material (or modified versions of it) with contractual assumptions of liability to the recipient, for any liability that these contractual assumptions directly impose on those licensors and authors. All other non-permissive additional terms are considered "further restrictions" within the meaning of section 10. If the Program as you received it, or any part of it, contains a notice stating that it is governed by this License along with a term that is a further restriction, you may remove that term. If a license document contains a further restriction but permits relicensing or conveying under this License, you may add to a covered work material governed by the terms of that license document, provided that the further restriction does not survive such relicensing or conveying. If you add terms to a covered work in accord with this section, you must place, in the relevant source files, a statement of the additional terms that apply to those files, or a notice indicating where to find the applicable terms. Additional terms, permissive or non-permissive, may be stated in the form of a separately written license, or stated as exceptions; the above requirements apply either way. 8. Termination. You may not propagate or modify a covered work except as expressly provided under this License. Any attempt otherwise to propagate or modify it is void, and will automatically terminate your rights under this License (including any patent licenses granted under the third paragraph of section 11). However, if you cease all violation of this License, then your license from a particular copyright holder is reinstated (a) provisionally, unless and until the copyright holder explicitly and finally terminates your license, and (b) permanently, if the copyright holder fails to notify you of the violation by some reasonable means prior to 60 days after the cessation. Moreover, your license from a particular copyright holder is reinstated permanently if the copyright holder notifies you of the violation by some reasonable means, this is the first time you have received notice of violation of this License (for any work) from that copyright holder, and you cure the violation prior to 30 days after your receipt of the notice. Termination of your rights under this section does not terminate the licenses of parties who have received copies or rights from you under this License. If your rights have been terminated and not permanently reinstated, you do not qualify to receive new licenses for the same material under section 10. 9. Acceptance Not Required for Having Copies. You are not required to accept this License in order to receive or run a copy of the Program. Ancillary propagation of a covered work occurring solely as a consequence of using peer-to-peer transmission to receive a copy likewise does not require acceptance. However, nothing other than this License grants you permission to propagate or modify any covered work. These actions infringe copyright if you do not accept this License. Therefore, by modifying or propagating a covered work, you indicate your acceptance of this License to do so. 10. Automatic Licensing of Downstream Recipients. Each time you convey a covered work, the recipient automatically receives a license from the original licensors, to run, modify and propagate that work, subject to this License. You are not responsible for enforcing compliance by third parties with this License. An "entity transaction" is a transaction transferring control of an organization, or substantially all assets of one, or subdividing an organization, or merging organizations. If propagation of a covered work results from an entity transaction, each party to that transaction who receives a copy of the work also receives whatever licenses to the work the party's predecessor in interest had or could give under the previous paragraph, plus a right to possession of the Corresponding Source of the work from the predecessor in interest, if the predecessor has it or can get it with reasonable efforts. You may not impose any further restrictions on the exercise of the rights granted or affirmed under this License. For example, you may not impose a license fee, royalty, or other charge for exercise of rights granted under this License, and you may not initiate litigation (including a cross-claim or counterclaim in a lawsuit) alleging that any patent claim is infringed by making, using, selling, offering for sale, or importing the Program or any portion of it. 11. Patents. A "contributor" is a copyright holder who authorizes use under this License of the Program or a work on which the Program is based. The work thus licensed is called the contributor's "contributor version". A contributor's "essential patent claims" are all patent claims owned or controlled by the contributor, whether already acquired or hereafter acquired, that would be infringed by some manner, permitted by this License, of making, using, or selling its contributor version, but do not include claims that would be infringed only as a consequence of further modification of the contributor version. For purposes of this definition, "control" includes the right to grant patent sublicenses in a manner consistent with the requirements of this License. Each contributor grants you a non-exclusive, worldwide, royalty-free patent license under the contributor's essential patent claims, to make, use, sell, offer for sale, import and otherwise run, modify and propagate the contents of its contributor version. In the following three paragraphs, a "patent license" is any express agreement or commitment, however denominated, not to enforce a patent (such as an express permission to practice a patent or covenant not to sue for patent infringement). To "grant" such a patent license to a party means to make such an agreement or commitment not to enforce a patent against the party. If you convey a covered work, knowingly relying on a patent license, and the Corresponding Source of the work is not available for anyone to copy, free of charge and under the terms of this License, through a publicly available network server or other readily accessible means, then you must either (1) cause the Corresponding Source to be so available, or (2) arrange to deprive yourself of the benefit of the patent license for this particular work, or (3) arrange, in a manner consistent with the requirements of this License, to extend the patent license to downstream recipients. "Knowingly relying" means you have actual knowledge that, but for the patent license, your conveying the covered work in a country, or your recipient's use of the covered work in a country, would infringe one or more identifiable patents in that country that you have reason to believe are valid. If, pursuant to or in connection with a single transaction or arrangement, you convey, or propagate by procuring conveyance of, a covered work, and grant a patent license to some of the parties receiving the covered work authorizing them to use, propagate, modify or convey a specific copy of the covered work, then the patent license you grant is automatically extended to all recipients of the covered work and works based on it. A patent license is "discriminatory" if it does not include within the scope of its coverage, prohibits the exercise of, or is conditioned on the non-exercise of one or more of the rights that are specifically granted under this License. You may not convey a covered work if you are a party to an arrangement with a third party that is in the business of distributing software, under which you make payment to the third party based on the extent of your activity of conveying the work, and under which the third party grants, to any of the parties who would receive the covered work from you, a discriminatory patent license (a) in connection with copies of the covered work conveyed by you (or copies made from those copies), or (b) primarily for and in connection with specific products or compilations that contain the covered work, unless you entered into that arrangement, or that patent license was granted, prior to 28 March 2007. Nothing in this License shall be construed as excluding or limiting any implied license or other defenses to infringement that may otherwise be available to you under applicable patent law. 12. No Surrender of Others' Freedom. If conditions are imposed on you (whether by court order, agreement or otherwise) that contradict the conditions of this License, they do not excuse you from the conditions of this License. If you cannot convey a covered work so as to satisfy simultaneously your obligations under this License and any other pertinent obligations, then as a consequence you may not convey it at all. For example, if you agree to terms that obligate you to collect a royalty for further conveying from those to whom you convey the Program, the only way you could satisfy both those terms and this License would be to refrain entirely from conveying the Program. 13. Use with the GNU Affero General Public License. Notwithstanding any other provision of this License, you have permission to link or combine any covered work with a work licensed under version 3 of the GNU Affero General Public License into a single combined work, and to convey the resulting work. The terms of this License will continue to apply to the part which is the covered work, but the special requirements of the GNU Affero General Public License, section 13, concerning interaction through a network will apply to the combination as such. 14. Revised Versions of this License. The Free Software Foundation may publish revised and/or new versions of the GNU General Public License from time to time. Such new versions will be similar in spirit to the present version, but may differ in detail to address new problems or concerns. Each version is given a distinguishing version number. If the Program specifies that a certain numbered version of the GNU General Public License "or any later version" applies to it, you have the option of following the terms and conditions either of that numbered version or of any later version published by the Free Software Foundation. If the Program does not specify a version number of the GNU General Public License, you may choose any version ever published by the Free Software Foundation. If the Program specifies that a proxy can decide which future versions of the GNU General Public License can be used, that proxy's public statement of acceptance of a version permanently authorizes you to choose that version for the Program. Later license versions may give you additional or different permissions. However, no additional obligations are imposed on any author or copyright holder as a result of your choosing to follow a later version. 15. Disclaimer of Warranty. THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. 16. Limitation of Liability. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. 17. Interpretation of Sections 15 and 16. If the disclaimer of warranty and limitation of liability provided above cannot be given local legal effect according to their terms, reviewing courts shall apply local law that most closely approximates an absolute waiver of all civil liability in connection with the Program, unless a warranty or assumption of liability accompanies a copy of the Program in return for a fee. END OF TERMS AND CONDITIONS How to Apply These Terms to Your New Programs If you develop a new program, and you want it to be of the greatest possible use to the public, the best way to achieve this is to make it free software which everyone can redistribute and change under these terms. To do so, attach the following notices to the program. It is safest to attach them to the start of each source file to most effectively state the exclusion of warranty; and each file should have at least the "copyright" line and a pointer to where the full notice is found. Copyright (C) This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . Also add information on how to contact you by electronic and paper mail. If the program does terminal interaction, make it output a short notice like this when it starts in an interactive mode: Copyright (C) This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'. This is free software, and you are welcome to redistribute it under certain conditions; type `show c' for details. The hypothetical commands `show w' and `show c' should show the appropriate parts of the General Public License. Of course, your program's commands might be different; for a GUI interface, you would use an "about box". You should also get your employer (if you work as a programmer) or school, if any, to sign a "copyright disclaimer" for the program, if necessary. For more information on this, and how to apply and follow the GNU GPL, see . The GNU General Public License does not permit incorporating your program into proprietary programs. If your program is a subroutine library, you may consider it more useful to permit linking proprietary applications with the library. If this is what you want to do, use the GNU Lesser General Public License instead of this License. But first, please read . mosh-1.2.5/COPYING.iOS000066400000000000000000000013631255051512600142160ustar00rootroot00000000000000The Mosh developers are aware that the terms of service that apply to apps distributed via Apple's App Store services may conflict with rights granted under Mosh's license, the GNU General Public License, version 3. The Mosh copyright holders do not wish this conflict to prevent the otherwise-compliant distribution of Mosh-derived apps via the App Store. Therefore, we have committed not to pursue any license violation that results solely from the conflict between the GPLv3 and the Apple App Store terms of service. In other words, as long as you comply with the GPL in all other respects, including its requirements to provide users with source code and the text of the license, we will not object to your distribution of Mosh through the App Store. mosh-1.2.5/ChangeLog000066400000000000000000000251351255051512600142470ustar00rootroot000000000000002015-07-12 Keith Winstein * Version 1.2.5 released. * New features: * Bind to a specific IP address with --bind-server. (Philipp Haselwarter) * MOSH_ESCAPE_KEY configures escape character. (Timo J. Rinne) * Support non-roaming IPv6. (Anders Kaseorg) * Implement XTerm mouse mode. (Barosl LEE, Andrew Chin, Louis Kruger) * Report Git revision along with version if available. (John Hood) * Platform support: * Add pselect() emulation. (Jérémie Courrèges-Anglas) * OpenBSD, OS X: Fix be64toh-related issues. (Jérémie Courrèges-Anglas) * ARM Neon: fix gcc4.8 compiling problem(Pasi Sjöholm) * NaCl: Conditionally rename main to mosh_main. (Richard Woodbury) * FreeBSD: Token pasting, forkpty(), ARM fixes. (John Hood) * AIX: Implement CTTY grabbing when TIOCSCTTY is missing (Anton Lundin) * OS X: Broaden build support to cover OS X 10.5 through 10.10. (John Hood) * Debian: Improve bash-completion install and functionality. (Suggested by Gabriel Filion, John Hood) * Bug fixes: * Automake/autoconf workarounds. (Anders Kaseorg) * mosh-server: Allow startup without PTY. (Keith Winstein) * network.cc: Properly close old fd on Socket assignment operator. (Thanks to Igor Bukanov) * mosh-server: Allow startup with zero-window-size PTY. (Igor Bukanov) * AddrInfo: Fix error message generation when node == NULL (Anders Kaseorg) * Timestamp: Prevent integer overflow on Darwin PPC 32-bit (Anders Kaseorg) * scripts/mosh: Fix hang when remote closes the connection (Anders Kaseorg) * Fix issues with parsing of 256-color SGR sequences. (John Hood) * Numerous code hygiene, Coverity, and Clang static analyzer fixes. (Anders Kaseorg, Geoffrey Thomas, John Hood) 2013-03-27 Keith Winstein * Version 1.2.4 released. * New features: * Support port ranges with -p LOWPORT:HIGHPORT (Luke Mewburn) * Ctrl-^ Ctrl-Z suspends mosh client (Nikolai Zeldovich) * mm:ss display of lost-contact times (Kevin Ballard) * Show infobar with control chars when Ctrl-^ is typed * Put terminal in altscreen mode (Anders Kaseorg) * Tell automake/Debian pkg about our checks (Anders Kaseorg) * Platform support: * OS X: Script to build a universal package (Peter Iannucci) * FreeBSD: Fix build problems (Jimmy Olgeni) * AIX: port by Anton Lundin * Solaris with system curses (Anton Lundin) * Cygwin and others: eliminate use of IO::Pty (Anton Lundin) * Bug fixes: * Fix bug (introduced in 1.2.3) where server stays around if process quits while client is detached * Clean up spurious entries from detached sessions warning * Fix freeze when connectivity is one-directional for hours (reported by Axel Beckert) * Don't wipe title until a new one is set (sqweek) * Eliminate memory leaks and cppcheck warnings (Anders Kaseorg) 2012-10-19 Keith Winstein * Version 1.2.3 released. * Security improvements: * Use OpenSSL AES implementation * Update AES-OCB implementation (Keegan McAllister) * Don't let bad server dictate IP (Felix Groebert) * New features: * Client hops ports to survive challenging client-side firewall * Server stops sending to save client power (Daniel Drown) * Set DiffServ code point and ECN-capable (Dave Täht) * Slow down if explicit congestion notification received * Warn about unattached Mosh sessions on login * Compatible with KDE konsole (uses BEL to terminate OSC) * Improved heuristic about color of predicted characters * Bug fixes: * Improved performance on systems with expensive time * No longer choke on "ffff::" address for hosts with IPv6 * More conservative MTU and datagram sizing * Platform support: * Build on Solaris and IllumOS (Timo Sirainen, Ira Cooper) * Build on ARM with gcc 4.7 (Alexander Chernyakhovsky) * Licensing changes: * Allow distribution on Apple App Stores * Allow linking with OpenSSL 2012-06-12 Keith Winstein * Version 1.2.2 released. * Remove warning on out-of-order/duplicated datagrams * Add "experimental" prediction mode 2012-05-25 Keith Winstein * Version 1.2.1 released. * Improve performance on lossy links. * New diagnostic message when link is dead in only one direction. * Use less CPU when link is down. (Keegan McAllister) * Use much less CPU when application or mosh-server sends large repeat counts (resolves CVE-2012-2385, reported by Timo Juhani Lindfors). * Use less memory when mosh-server is malicious. * Fix vttest regression re: wrapping and tabs. * Enable roundtrip verifier of terminal emulator correctness when verbose. * Remove skalibs as a dependency. (Keegan McAllister) * Remove use of poll() and OS X poll workaround. (Keegan McAllister) * Include bash_completion file. (ejeffrey) * Include UFW firewall profile. (Fumihito YOSHIDA) * Warning on out-of-order/duplicated datagrams (or failed nonce increment). * Clearer error message on invalid port number. * Cleanups to quit scenario when firewalled. 2012-04-26 Keith Winstein * Version 1.2 released. * Remove Boost as a dependency (Keegan McAllister) * Add support for FreeBSD, Cygwin, RHEL/CentOS 5, OS X 10.5 on PPC. * More verbose and helpful diagnostics. Server now has -v flag. * Client now has --ssh= flag to set SSH options (like port) * Remove skalibs as a dependency on Debian/Ubuntu (Keegan McAllister) * Now passes locale-related env vars over the connection * Fix startup script to no longer hang on some Macs (Jay Freeman) * Fix terminal emulation and argument parsing on FreeBSD * Fix alignment problems on ARM * Now prints message of the day * Use binary hardening flags where available (Keegan McAllister) * Responsiveness and CPU-usage improvements * Several terminal-emulation improvements and bug fixes 2012-04-03 Keith Winstein * Version 1.1.3 released. * Builds on armel, armhf, and kFreeBSD. * Fixes bug causing occasional missing/extra wraparound copy-and-paste. * Eliminates valgrind complaint over unused winsize fields. * Close connection after four petabytes (per OCB recommendation). 2012-03-28 Keith Winstein * Version 1.1.2 released. * Fixes to build on OS X 10.5 with older gcc and -lpoll (Quentin Smith) * Add --with-utempter and --without-utempter per gentoo request (Michael Weber) * configure now requires ncurses headers (and others) to be installed * Consolidate locale routines to help Android port (Keegan McAllister) 2012-03-27 Keith Winstein * Version 1.1.1 released. * Fix spec file and build failure on Fedora. * Print out error message properly on fatal_assert(). * Support for machines without posix_memalign(). 2012-03-22 Keith Winstein * Version 1.1 released (stable release). * Allows user to specify remote command to execute. * Only advertises 256 colors when user's terminal has 256 colors. * Add chaff to datagrams to frustrate statistical analysis of length * Cosmetic fixes to terminal handling * Improved startup script (Anders Kaseorg) 2012-03-16 Keith Winstein * Version 1.0.2 released. * Uses xterm-256color and supports 256-color escape sequences. * Posterizes to 8 colors unless the user has a 256-color terminal. * Handles terminals without BCE. * Starts login shell. 2012-03-14 Keith Winstein * Version 1.0.1 released. * Roughly 40% less CPU usage. 2012-03-12 Keith Winstein * Version 1.0 released. * mosh now supports --version option and prints no-warranty message. 2012-03-10 Keith Winstein * Version 0.98 released. * Add option to select server-side UDP port. * Restrict default UDP port range to 60000..61000. * Use TERM / terminfo to decide when to send ECH sequence. * Now works properly inside tmux. 2012-03-07 Keith Winstein * Version 0.97 released. * Merged Mac OS X port (thanks to Quentin Smith and Anders Kaseorg) * Server will quit if no connection within first 60 seconds * Startup script no longer requires threaded Perl * Add --enable-compile-warnings=error to ./configure * Fix some flicker issues with adaptive prediction mode 2012-02-26 Keith Winstein * Version 0.96 released. * Compress all instructions with zlib 2012-02-25 Keith Winstein * Version 0.95 released. * Make echo acknowledgment reliable to reduce spurious mispredict detections. * Require two dropped heartbeats before showing blue bar of lost contact * Print newline before MOSH CONNECT string to make more robust if intermediate keys hit * Disable ControlMaster in initial SSH connection so proxy is always used * Make retransmissions occur at frame rate (vs. every 3 seconds) for limited time after loss 2012-02-15 Keith Winstein * Version 0.94 released. * Make man pages * Replace C++11 features (lambda and auto) with boost equivalents (Anders Kaseorg) * Now builds with g++ or clang (with libstdc++) 2012-02-13 Keith Winstein * Version 0.93 released. * Make utmp entries 2012-02-13 Keith Winstein * Version 0.92 released. * Allows user to select prediction mode (always/never/adaptive) * Fix bug in server startup on slow hosts * Better prediction when deleting at line ending 2012-02-09 Keith Winstein * Version 0.91 released. * Startup script support SSH options and aliases (Anders Kaseorg) * End use of Path MTU discovery and allow IP fragmentation because, e.g., Cisco VPN has MTU of 1200 and does not pass ICMP too-big. * Better exception and error handling. * Restrict predictive local echo display to long-delay links (>60 ms RTT) or for temporarily after a "glitch." Otherwise simply show server echos, while maintaining prediction overlay in the background in case it is needed. 2012-02-07 Keith Winstein * Version 0.9b released. * Improvements to build system (Anders Kaseorg) 2012-02-06 Keith Winstein * Version 0.9 released. mosh-1.2.5/Makefile.am000066400000000000000000000026161255051512600145300ustar00rootroot00000000000000ACLOCAL_AMFLAGS = -I m4 SUBDIRS = src scripts man conf EXTRA_DIST = autogen.sh ocb-license.html README.md COPYING.iOS BUILT_SOURCES = version.h CLANG_SCAN_BUILD = scan-build .PHONY: VERSION VERSION: @echo @PACKAGE_STRING@ > VERSION.dist @set -e; if git describe --dirty --always > VERSION.git 2>&1 && \ [ -z `git rev-parse --show-prefix` ]; then \ if ! diff -q VERSION.git VERSION > /dev/null 2>&1; then \ mv -f VERSION.git VERSION; \ fi; \ elif ! diff -q VERSION.dist VERSION > /dev/null 2>&1; then \ mv -f VERSION.dist VERSION; \ fi @rm -f VERSION.dist VERSION.git version.h: VERSION @printf '#define BUILD_VERSION "%s"\n' "$$(cat VERSION)" > version.h.new @set -e; if ! diff -q version.h version.h.new > /dev/null 2>&1; then \ mv -f version.h.new version.h; \ fi @rm -f version.h.new clean-local: @rm -rf version.h VERSION cov-int mosh-coverity.txz cppcheck: $(BUILT_SOURCES) config.h cppcheck --enable=all --template=gcc -include config.h -I . \ -I src/crypto -I src/frontend -I src/network -I src/protobufs \ -I src/statesync -I src/terminal -I src/util \ -I /usr/include -I /usr/include/google/protobuf -I/usr/include/openssl \ . cov-build: $(BUILT_SOURCES) config.h $(MAKE) clean && rm -rf cov-int && \ cov-build --dir cov-int $(MAKE) check && \ tar -caf mosh-coverity.txz cov-int scan-build: $(BUILT_SOURCES) config.h $(MAKE) clean && $(CLANG_SCAN_BUILD) $(MAKE) check mosh-1.2.5/NEWS000066400000000000000000000007301255051512600131660ustar00rootroot000000000000002012-04-26 ---------- * Version 1.2 released 2012-03-22 ---------- * Version 1.1 released 2012-03-12 ---------- * Version 1.0 released 2012-03-07 Release candidate (0.97): ------------------------------------ * Now builds on Mac OS X as well as GNU/Linux 2012-02-06 Initial release (0.9): --------------------------------- * Supports intermittent connectivity * Supports client and server IP roaming * Local echo supports typing and the left and right arrow keys mosh-1.2.5/README.md000066400000000000000000000142271255051512600137540ustar00rootroot00000000000000Mosh: the mobile shell ====================== Mosh is a remote terminal application that supports intermittent connectivity, allows roaming, and provides speculative local echo and line editing of user keystrokes. It aims to support the typical interactive uses of SSH, plus: * Mosh keeps the session alive if the client goes to sleep and wakes up later, or temporarily loses its Internet connection. * Mosh allows the client and server to "roam" and change IP addresses, while keeping the connection alive. Unlike SSH, Mosh can be used while switching between Wi-Fi networks or from Wi-Fi to cellular data to wired Ethernet. * The Mosh client runs a predictive model of the server's behavior in the background and tries to guess intelligently how each keystroke will affect the screen state. When it is confident in its predictions, it will show them to the user while waiting for confirmation from the server. Most typing and uses of the left- and right-arrow keys can be echoed immediately. As a result, Mosh is usable on high-latency links, e.g. on a cellular data connection or spotty Wi-Fi. In distinction from previous attempts at local echo modes in other protocols, Mosh works properly with full-screen applications such as emacs, vi, alpine, and irssi, and automatically recovers from occasional prediction errors within an RTT. On high-latency links, Mosh underlines its predictions while they are outstanding and removes the underline when they are confirmed by the server. Mosh does not support X forwarding or the non-interactive uses of SSH, including port forwarding. Other features -------------- * Mosh adjusts its frame rate so as not to fill up network queues on slow links, so "Control-C" always works within an RTT to halt a runaway process. * Mosh warns the user when it has not heard from the server in a while. * Mosh supports lossy links that lose a significant fraction of their packets. * Mosh handles some Unicode edge cases better than SSH and existing terminal emulators by themselves, but requires a UTF-8 environment to run. * Mosh leverages SSH to set up the connection and authenticate users. Mosh does not contain any privileged (root) code. Getting Mosh ------------ [The Mosh web site](http://mosh.mit.edu/#getting) has information about packages for many operating systems, as well as instructions for building from source. Note that `mosh-client` receives an AES session key as an environment variable. If you are porting Mosh to a new operating system, please make sure that a running process's environment variables are not readable by other users. We have confirmed that this is the case on GNU/Linux, OS X, and FreeBSD. Usage ----- The `mosh-client` binary must exist on the user's machine, and the `mosh-server` binary on the remote host. The user runs: $ mosh [user@]host If the `mosh-client` or `mosh-server` binaries live outside the user's `$PATH`, `mosh` accepts the arguments `--client=PATH` and `--server=PATH` to select alternate locations. More options are documented in the mosh(1) manual page. There are [more examples](http://mosh.mit.edu/#usage) and a [FAQ](http://mosh.mit.edu/#faq) on the Mosh web site. How it works ------------ The `mosh` program will SSH to `user@host` to establish the connection. SSH may prompt the user for a password or use public-key authentication to log in. From this point, `mosh` runs the `mosh-server` process (as the user) on the server machine. The server process listens on a high UDP port and sends its port number and an AES-128 secret key back to the client over SSH. The SSH connection is then shut down and the terminal session begins over UDP. If the client changes IP addresses, the server will begin sending to the client on the new IP address within a few seconds. To function, Mosh requires UDP datagrams to be passed between client and server. By default, `mosh` uses a port number between 60000 and 61000, but the user can select a particular port with the -p option. Advice to distributors ---------------------- A note on compiler flags: Mosh is security-sensitive code. When making automated builds for a binary package, we recommend passing the option `--enable-compile-warnings=error` to `./configure`. On GNU/Linux with `g++` or `clang++`, the package should compile cleanly with `-Werror`. Please report a bug if it doesn't. Where available, Mosh builds with a variety of binary hardening flags such as `-fstack-protector-all`, `-D_FORTIFY_SOURCE=2`, etc. These provide proactive security against the possibility of a memory corruption bug in Mosh or one of the libraries it uses. For a full list of flags, search for `HARDEN` in `configure.ac`. The `configure` script detects which flags are supported by your compiler, and enables them automatically. To disable this detection, pass `--disable-hardening` to `./configure`. Please report a bug if you have trouble with the default settings; we would like as many users as possible to be running a configuration as secure as possible. Mosh ships with a default optimization setting of `-O2`. Some distributors have asked about changing this to `-Os` (which causes a compiler to prefer space optimizations to time optimizations). We have benchmarked with the included `src/examples/benchmark` program to test this. The results are that `-O2` is 40% faster than `-Os` with g++ 4.6 on GNU/Linux, and 16% faster than `-Os` with clang++ 3.1 on Mac OS X. In both cases, `-Os` did produce a smaller binary (by up to 40%, saving almost 200 kilobytes on disk). While Mosh is not especially CPU intensive and mostly sits idle when the user is not typing, we think the results suggest that `-O2` (the default) is preferable. More info --------- * Mosh Web site: * `mosh-devel@mit.edu` mailing list: * `mosh-users@mit.edu` mailing list: * `#mosh` channel on [Freenode IRC](http://freenode.net/) http://webchat.freenode.net/?channels=mosh mosh-1.2.5/THANKS000066400000000000000000000021641255051512600134050ustar00rootroot00000000000000* Hari Balakrishnan, who advised this work and came up with the name. * Paul Williams (www.vt100.net), whose reverse-engineered vt500 state diagram is the basis for the Mosh parser. * The anonymous users who contributed session logs for tuning and measuring Mosh's predictive echo. * Nickolai Zeldovich for helpful comments on the Mosh research paper. * Richard Stallman for helpful discussion about the capabilities of the SUPDUP Local Editing Protocol. * Nelson Elhage * Christine Spang * Stefie Tellex * Joseph Sokol-Margolis * Waseem Daher * Bill McCloskey * Austin Roach * Greg Hudson * Karl Ramm * Alexander Chernyakhovsky * Peter Iannucci * Evan Broder * Neha Narula * Katrina LaCurts * Ramesh Chandra * Peter Jeremy * Ed Schouten * Ryan Steinmetz * Jay Freeman * Dave Täht * Larry Doolittle * Daniel Drown * Timo Juhani Lindfors * Timo Sirainen * Ira Cooper * Felix Gröbert * Luke Mewburn * Anton Lundin * Philipp Haselwarter * Timo J. Rinne * Barosl Lee * Andrew Chin * Louis Kruger * Jérémie Courrèges-Anglas * Pasi Sjöholm * Richard Woodbury * Igor Bukanov * Geoffrey Thomas mosh-1.2.5/autogen.sh000077500000000000000000000000371255051512600144700ustar00rootroot00000000000000#!/bin/sh exec autoreconf -fi mosh-1.2.5/build-package.sh000077500000000000000000000001241255051512600155130ustar00rootroot00000000000000#!/bin/sh git-buildpackage --git-upstream-branch=master --git-upstream-tree=branch mosh-1.2.5/conf/000077500000000000000000000000001255051512600134145ustar00rootroot00000000000000mosh-1.2.5/conf/Makefile.am000066400000000000000000000003671255051512600154560ustar00rootroot00000000000000completionsdir = @completions@ dist_completions_DATA = nobase_dist_sysconf_DATA = if INSTALL_UFW nobase_dist_sysconf_DATA += ufw/applications.d/mosh endif if INSTALL_COMPLETION dist_completions_DATA += bash-completion/completions/mosh endif mosh-1.2.5/conf/bash-completion/000077500000000000000000000000001255051512600165005ustar00rootroot00000000000000mosh-1.2.5/conf/bash-completion/completions/000077500000000000000000000000001255051512600210345ustar00rootroot00000000000000mosh-1.2.5/conf/bash-completion/completions/mosh000066400000000000000000000001711255051512600217240ustar00rootroot00000000000000_mosh () { local cur prev _init_completion || return _known_hosts_real -a "$cur" } complete -F _mosh mosh mosh-1.2.5/conf/ufw/000077500000000000000000000000001255051512600142155ustar00rootroot00000000000000mosh-1.2.5/conf/ufw/applications.d/000077500000000000000000000000001255051512600171255ustar00rootroot00000000000000mosh-1.2.5/conf/ufw/applications.d/mosh000066400000000000000000000002011255051512600200070ustar00rootroot00000000000000[mosh] title=Mosh (mobile shell) description=Mobile shell that supports roaming and intelligent local echo ports=60000:61000/udp mosh-1.2.5/configure.ac000066400000000000000000000310261255051512600147570ustar00rootroot00000000000000# -*- Autoconf -*- # Process this file with autoconf to produce a configure script. AC_PREREQ([2.61]) AC_INIT([mosh], [1.2.5], [mosh-devel@mit.edu]) AM_INIT_AUTOMAKE([foreign -Wall -Werror]) m4_ifdef([AM_SILENT_RULES], [AM_SILENT_RULES([yes])]) AC_CONFIG_SRCDIR([src/frontend/mosh-client.cc]) AC_CONFIG_MACRO_DIR([m4]) AC_CONFIG_HEADERS([config.h]) # Checks for programs. AC_PROG_CC AC_PROG_CXX AC_PROG_RANLIB AC_PATH_PROG([PROTOC], [protoc], []) AS_IF([test x"$PROTOC" = x], [AC_MSG_ERROR([cannot find protoc, the Protocol Buffers compiler])]) # automake 1.12 seems to require this, but automake 1.11 doesn't recognize it m4_ifdef([AM_PROG_AR], [AM_PROG_AR]) WARNING_CXXFLAGS="" PICKY_CXXFLAGS="" AC_ARG_ENABLE([compile-warnings], [AS_HELP_STRING([--enable-compile-warnings@<:@=no/yes/maximum/error@:>@], [Turn on compiler warnings])], [case "$enableval" in no) ;; '' | yes) WARNING_CXXFLAGS="-Wall" ;; maximum) WARNING_CXXFLAGS="-Wall" PICKY_CXXFLAGS="-Wextra -pedantic -Wno-long-long -Weffc++ -Wmissing-declarations" ;; error) WARNING_CXXFLAGS="-Wall -Werror" PICKY_CXXFLAGS="-Wextra -pedantic -Wno-long-long -Weffc++ -Wmissing-declarations" ;; *) AC_MSG_ERROR([Unknown argument '$enableval' to --enable-compile-warnings]) ;; esac], [WARNING_CXXFLAGS="-Wall"]) AC_SUBST([WARNING_CXXFLAGS]) AC_SUBST([PICKY_CXXFLAGS]) # We want to check for compiler flag support, but there is no way to make # clang's "argument unused" warning fatal. So we invoke the compiler through a # wrapper script that greps for this message. saved_CC="$CC" saved_CXX="$CXX" saved_LD="$LD" flag_wrap="$srcdir/scripts/wrap-compiler-for-flag-check" CC="$flag_wrap $CC" CXX="$flag_wrap $CXX" LD="$flag_wrap $LD" # We use the same hardening flags for C and C++. We must check that each flag # is supported by both compilers. AC_DEFUN([check_cc_cxx_flag], [AC_LANG_PUSH(C) AX_CHECK_COMPILE_FLAG([$1], [AC_LANG_PUSH(C++) AX_CHECK_COMPILE_FLAG([$1], [$2], [$3], [-Werror $4]) AC_LANG_POP(C++)], [$3], [-Werror $4]) AC_LANG_POP(C)]) AC_DEFUN([check_link_flag], [AX_CHECK_LINK_FLAG([$1], [$2], [$3], [-Werror $4])]) AC_ARG_ENABLE([hardening], [AS_HELP_STRING([--enable-hardening], [Enable compiler and linker options to frustrate memory corruption exploits @<:@yes@:>@])], [hardening="$enableval"], [hardening="yes"]) HARDEN_CFLAGS="" HARDEN_LDFLAGS="" AS_IF([test x"$hardening" != x"no"], [ check_cc_cxx_flag([-fno-strict-overflow], [HARDEN_CFLAGS="$HARDEN_CFLAGS -fno-strict-overflow"]) # This one will likely succeed, even on platforms where it does nothing. check_cc_cxx_flag([-D_FORTIFY_SOURCE=2], [HARDEN_CFLAGS="$HARDEN_CFLAGS -D_FORTIFY_SOURCE=2"]) check_cc_cxx_flag([-fstack-protector-all], [HARDEN_CFLAGS="$HARDEN_CFLAGS -fstack-protector-all" check_cc_cxx_flag([-Wstack-protector], [HARDEN_CFLAGS="$HARDEN_CFLAGS -Wstack-protector"], [], [-fstack-protector-all]) check_cc_cxx_flag([--param ssp-buffer-size=1], [HARDEN_CFLAGS="$HARDEN_CFLAGS --param ssp-buffer-size=1"], [], [-fstack-protector-all])]) check_cc_cxx_flag([-fPIE], [check_link_flag([-fPIE -pie], [HARDEN_CFLAGS="$HARDEN_CFLAGS -fPIE" HARDEN_LDFLAGS="$HARDEN_LDFLAGS -pie"], [check_link_flag([-fPIE -Wl,-pie], [HARDEN_CFLAGS="$HARDEN_CFLAGS -fPIE" HARDEN_LDFLAGS="$HARDEN_LDFLAGS -Wl,-pie"])])]) check_link_flag([-Wl,-z,relro], [HARDEN_LDFLAGS="$HARDEN_LDFLAGS -Wl,-z,relro" check_link_flag([-Wl,-z,now], [HARDEN_LDFLAGS="$HARDEN_LDFLAGS -Wl,-z,now"])])]) AC_SUBST([HARDEN_CFLAGS]) AC_SUBST([HARDEN_LDFLAGS]) # Also check for a few non-hardening-related flags. MISC_CXXFLAGS="" AC_LANG_PUSH(C++) AX_CHECK_COMPILE_FLAG([-fno-default-inline], [MISC_CXXFLAGS="$MISC_CXXFLAGS -fno-default-inline"], [], [-Werror]) AX_CHECK_COMPILE_FLAG([-pipe], [MISC_CXXFLAGS="$MISC_CXXFLAGS -pipe"], [], [-Werror]) AC_LANG_POP(C++) AC_SUBST([MISC_CXXFLAGS]) # End of flag tests. CC="$saved_CC" CXX="$saved_CXX" LD="$saved_LD" AC_ARG_ENABLE([client], [AS_HELP_STRING([--enable-client], [Build the mosh-client program @<:@yes@:>@])], [build_client="$enableval"], [build_client="yes"]) AM_CONDITIONAL([BUILD_CLIENT], [test x"$build_client" != xno]) AC_ARG_ENABLE([server], [AS_HELP_STRING([--enable-server], [Build the mosh-server program @<:@yes@:>@])], [build_server="$enableval"], [build_server="yes"]) AM_CONDITIONAL([BUILD_SERVER], [test x"$build_server" != xno]) AC_ARG_ENABLE([examples], [AS_HELP_STRING([--enable-examples], [Build the miscellaneous programs in src/examples @<:@no@:>@])], [build_examples="$enableval"], [build_examples="no"]) AM_CONDITIONAL([BUILD_EXAMPLES], [test x"$build_examples" != xno]) AC_ARG_ENABLE([ufw], [AS_HELP_STRING([--enable-ufw], [Install firewall profile for ufw (Uncomplicated Firewall) @<:@no@:>@])], [install_ufw="$enableval"], [install_ufw="no"]) AM_CONDITIONAL([INSTALL_UFW], [test x"$install_ufw" != xno]) AC_ARG_ENABLE([completion], [AS_HELP_STRING([--enable-completion], [Install bash_completion rule @<:@no@:>@])], [install_completion="$enableval"], [install_completion="no"]) AM_CONDITIONAL([INSTALL_COMPLETION], [test x"$install_completion" != xno]) # Checks for libraries. AC_ARG_WITH([utempter], [AS_HELP_STRING([--with-utempter], [write utmp entries using libutempter @<:@check@:>@])], [with_utempter="$withval"], [with_utempter="check"]) AS_IF([test x"$with_utempter" != xno], [AC_SEARCH_LIBS([utempter_remove_record], [utempter], [AC_DEFINE([HAVE_UTEMPTER], [1], [Define if libutempter is available.])], [AS_IF([test x"$with_utempter" = xcheck], [AC_MSG_WARN([Unable to find libutempter; utmp entries will not be made.])], [AC_MSG_ERROR([--with-utempter was given but libutempter was not found.])])])]) AC_SEARCH_LIBS([compress], [z], , [AC_MSG_ERROR([Unable to find zlib.])]) AC_SEARCH_LIBS([socket], [socket]) AC_SEARCH_LIBS([inet_addr], [nsl]) # Checks for header files. AC_CHECK_HEADERS(m4_normalize([ fcntl.h langinfo.h limits.h locale.h netdb.h netinet/in.h stddef.h stdint.h inttypes.h stdlib.h string.h sys/ioctl.h sys/resource.h sys/socket.h sys/stat.h sys/time.h termios.h unistd.h wchar.h wctype.h ]), [], [AC_MSG_ERROR([Missing required header file.])]) AC_CHECK_HEADERS([pty.h util.h libutil.h paths.h]) AC_CHECK_HEADERS([endian.h sys/endian.h]) AC_CHECK_HEADERS([utmpx.h]) AC_CHECK_HEADERS([termio.h]) AC_CHECK_HEADERS([sys/uio.h]) # Checks for typedefs, structures, and compiler characteristics. AC_HEADER_STDBOOL AC_C_INLINE AC_TYPE_INT64_T AC_TYPE_PID_T AC_C_RESTRICT AC_TYPE_SIZE_T AC_TYPE_SSIZE_T AC_TYPE_UINT16_T AC_TYPE_UINT32_T AC_TYPE_UINT64_T AC_TYPE_UINT8_T AC_TYPE_UINTPTR_T # Checks for library functions. AC_FUNC_FORK AC_FUNC_MBRTOWC AC_CHECK_FUNCS(m4_normalize([ gettimeofday setrlimit iswprint memchr memset nl_langinfo posix_memalign setenv setlocale sigaction socket strchr strdup strncasecmp strtok strerror strtol wcwidth cfmakeraw pselect getaddrinfo getnameinfo ])) AC_SEARCH_LIBS([clock_gettime], [rt], [AC_DEFINE([HAVE_CLOCK_GETTIME], [1], [Define if clock_gettime is available.])]) PKG_CHECK_MODULES([OPENSSL], [openssl]) # Start by trying to find the needed tinfo parts by pkg-config PKG_CHECK_MODULES([TINFO], [tinfo], [AC_DEFINE([HAVE_CURSES_H], [1], [Define to 1 if is present])], [PKG_CHECK_MODULES([TINFO], [ncurses], [AC_DEFINE([HAVE_CURSES_H], [1], [Define to 1 if is present])], [AX_CHECK_LIBRARY([TINFO], [curses.h], [tinfo], [AC_DEFINE([HAVE_CURSES_H], [1], [Define to 1 if is present]) AC_SUBST([TINFO_CFLAGS], ["$TINFO_CPPFLAGS"]) AC_SUBST([TINFO_LIBS], ["$TINFO_LDFLAGS -ltinfo"])],)])]) # Then try to find it in a specific install dir AC_ARG_WITH(curses, AC_HELP_STRING([--with-curses=DIR], [Where curses is installed]), [if test $withval != yes; then cv_curses=$withval fi if test x$cv_curses != x/usr; then CURSES_LDFLAGS="-L${cv_curses}/lib" CURSES_CPPFLAGS="-I${cv_curses}/include" fi]) if test "x$ax_cv_have_TINFO" = xno ; then # save away old LDFLAGS/CPPFLAGS and check for curses with cv_curses old_LDFLAGS="$LDFLAGS" old_CPPFLAGS="$CPPFLAGS" LDFLAGS="$LDFLAGS $CURSES_LDFLAGS" CPPFLAGS="$LDFLAGS $CURSES_CPPFLAGS" AX_WITH_CURSES # restore old LDFLAGS/CPPFLAGS LDFLAGS="$old_LDFLAGS" CPPFLAGS="$old_CPPFLAGS" if test "x$ax_cv_curses_which" = xno ; then AC_MSG_ERROR([requires either tinfo, NcursesW or Ncurses library]) else AC_SUBST([TINFO_LIBS], ["$CURSES_LDFLAGS $CURSES_LIB"]) AC_SUBST([TINFO_CFLAGS], ["$CURSES_CPPFLAGS"]) fi fi AC_CHECK_DECL([forkpty], [AC_DEFINE([FORKPTY_IN_LIBUTIL], [1], [Define if libutil.h necessary for forkpty().])], , [[#include #include ]]) AC_CHECK_LIB([util], [forkpty], [ AC_DEFINE([HAVE_FORKPTY],, [Define if you have forkpty().]) LIBUTIL="-lutil" AC_SUBST([LIBUTIL]) ]) AC_MSG_CHECKING([whether pipe2(..., O_CLOEXEC) is supported]) AC_LINK_IFELSE([AC_LANG_PROGRAM([[#define _GNU_SOURCE #include #include int pipefd[2]; ]], [[(void) pipe2(pipefd, O_CLOEXEC);]])], [AC_DEFINE([HAVE_PIPE2_CLOEXEC], [1], [Define if pipe2(..., O_CLOEXEC) is available.]) AC_MSG_RESULT([yes])], [AC_MSG_RESULT([no])]) AC_MSG_CHECKING([whether FD_ISSET() argument is const]) AC_LANG_PUSH(C++) AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[#include class T { public: fd_set fds; bool Fun( void ) const { return FD_ISSET( 0, &fds ); } };]], [[T x; return x.Fun();]])], [AC_DEFINE([FD_ISSET_IS_CONST], [1], [Define if FD_ISSET() fd_set argument is const.]) AC_MSG_RESULT([yes])], [AC_MSG_RESULT([no])]) AC_LANG_POP(C++) AC_CHECK_DECLS([__builtin_bswap64, __builtin_ctz]) AC_CHECK_DECL([mach_absolute_time], [AC_DEFINE([HAVE_MACH_ABSOLUTE_TIME], [1], [Define if mach_absolute_time is available.])], , [[#include ]]) AC_CHECK_DECLS([be64toh, betoh64], [], [], [[#if defined(HAVE_ENDIAN_H) #include #elif defined(HAVE_SYS_ENDIAN_H) #include #include #endif]]) AS_IF([test x"$ac_cv_have_decl_be64toh" != xyes && test x"$ac_cv_have_decl_betoh64" != xyes], [AC_CHECK_DECL([OSSwapHostToBigInt64], [AC_DEFINE([HAVE_OSX_SWAP], [1], [Define if OSSwapHostToBigInt64 and friends exist.])], [AC_MSG_WARN([Unable to find byte swapping functions; using built-in routines.])], [[#include ]])]) AC_CHECK_DECL([IP_MTU_DISCOVER], [AC_DEFINE([HAVE_IP_MTU_DISCOVER], [1], [Define if IP_MTU_DISCOVER is a valid sockopt.])], , [[#include ]]) AC_CHECK_DECL([IP_RECVTOS], [AC_DEFINE([HAVE_IP_RECVTOS], [1], [Define if IP_RECVTOS is a valid sockopt.])], , [[#include ]]) AC_CHECK_DECL([__STDC_ISO_10646__], [], [AC_MSG_WARN([C library doesn't advertise wchar_t is Unicode (OS X works anyway with workaround).])], [[#include ]]) AC_CHECK_DECL([IUTF8], [AC_DEFINE([HAVE_IUTF8], [1], [Define if IUTF8 is a defined termios mode.])], [AC_MSG_WARN([No IUTF8 termios mode; character-erase of multibyte character sequence probably does not work properly in canonical mode on this platform.])], [[#include ]]) # Checks for protobuf PKG_CHECK_MODULES([protobuf], [protobuf]) # Bash completion needs to ask where it goes if >= 2.0 is installed. AS_IF([test "$install_completion" != no], [PKG_CHECK_MODULES([BASH_COMPLETION], [bash-completion >= 2.0], [if test "$prefix" = "NONE"; then completions="`pkg-config --variable=completionsdir bash-completion`" else completions="`pkg-config --define-variable=prefix=$prefix --variable=completionsdir bash-completion`" fi], [completions="${sysconfdir}/bash_completion.d"]) AC_SUBST([completions])]) AC_CONFIG_FILES([ Makefile src/Makefile src/crypto/Makefile src/frontend/Makefile src/network/Makefile src/protobufs/Makefile src/statesync/Makefile src/terminal/Makefile src/util/Makefile scripts/Makefile src/examples/Makefile src/tests/Makefile man/Makefile conf/Makefile ]) AC_OUTPUT AC_MSG_NOTICE([ === Configuration results ===]) AC_MSG_NOTICE([Version: $PACKAGE_VERSION]) AC_MSG_NOTICE([c++ compiler: $CXX]) AC_MSG_NOTICE([Warning CXXFLAGS: $WARNING_CXXFLAGS]) AC_MSG_NOTICE([Picky CXXFLAGS: $PICKY_CXXFLAGS]) AC_MSG_NOTICE([Harden CFLAGS: $HARDEN_CFLAGS]) AC_MSG_NOTICE([ =============================]) mosh-1.2.5/debian/000077500000000000000000000000001255051512600137115ustar00rootroot00000000000000mosh-1.2.5/debian/changelog000066400000000000000000000457771255051512600156070ustar00rootroot00000000000000mosh (1.2.5-1) unstable; urgency=low * Version 1.2.5 released. -- Keith Winstein Sun, 12 Jul 2015 12:42:00 -0400 mosh (1.2.4.95rc2-1) unstable; urgency=low * Version 1.2.5 release candidate. * New features: * Bind to a specific IP address with --bind-server. (Philipp Haselwarter) * MOSH_ESCAPE_KEY configures escape character. (Timo J. Rinne) * Support non-roaming IPv6. (Anders Kaseorg) * Implement XTerm mouse mode. (Barosl LEE, Andrew Chin, Louis Kruger) * Report Git revision along with version if available. (John Hood) * Platform support: * Add pselect() emulation. (Jérémie Courrèges-Anglas) * OpenBSD, OS X: Fix be64toh-related issues. (Jérémie Courrèges-Anglas) * ARM Neon: fix gcc4.8 compiling problem(Pasi Sjöholm) * NaCl: Conditionally rename main to mosh_main. (Richard Woodbury) * FreeBSD: Token pasting, forkpty(), ARM fixes. (John Hood) * AIX: Implement CTTY grabbing when TIOCSCTTY is missing (Anton Lundin) * OS X: Broaden build support to cover OS X 10.5 through 10.10. (John Hood) * Debian: Improve bash-completion install and functionality. (Suggested by Gabriel Filion, John Hood) * Bug fixes: * Automake/autoconf workarounds. (Anders Kaseorg) * mosh-server: Allow startup without PTY. (Keith Winstein) * network.cc: Properly close old fd on Socket assignment operator. (Thanks to Igor Bukanov) * mosh-server: Allow startup with zero-window-size PTY. (Igor Bukanov) * AddrInfo: Fix error message generation when node == NULL (Anders Kaseorg) * Timestamp: Prevent integer overflow on Darwin PPC 32-bit (Anders Kaseorg) * scripts/mosh: Fix hang when remote closes the connection (Anders Kaseorg) * Fix issues with parsing of 256-color SGR sequences. (John Hood) * Numerous code hygiene, Coverity, and Clang static analyzer fixes. (Anders Kaseorg, Geoffrey Thomas, John Hood) -- Keith Winstein Mon, 08 Jun 2015 22:45:00 -0400 mosh (1.2.4a-1) unstable; urgency=low * Eliminate redundant ocb.cc test (fixes build warning on ARM/MIPS/s390) * Add explicit cast to int (fixes build warning on older gcc) -- Keith Winstein Wed, 27 Mar 2013 18:32:33 -0400 mosh (1.2.4-1) unstable; urgency=low * Version 1.2.4 released. -- Keith Winstein Wed, 27 Mar 2013 00:17:39 -0400 mosh (1.2.3.95rc1-1) unstable; urgency=low * Version 1.2.4 release candidate * New features: * Support port ranges with -p LOWPORT:HIGHPORT (Luke Mewburn) * Ctrl-^ Ctrl-Z suspends mosh client (Nikolai Zeldovich) * mm:ss display of lost-contact times (Kevin Ballard) * Show infobar with control chars when Ctrl-^ is typed * Put terminal in altscreen mode (Anders Kaseorg) * Tell automake/Debian pkg about our checks (Anders Kaseorg) * Platform support: * OS X: Script to build a universal package (Peter Iannucci) * FreeBSD: Fix build problems (Jimmy Olgeni) * AIX: port by Anton Lundin * Solaris with system curses (Anton Lundin) * Cygwin and others: eliminate use of IO::Pty (Anton Lundin) * Bug fixes: * Fix bug (introduced in 1.2.3) where server stays around if process quits while client is detached * Clean up spurious entries from detached sessions warning * Fix freeze when connectivity is one-directional for hours (reported by Axel Beckert) * Don't wipe title until a new one is set (sqweek) * Eliminate memory leaks and cppcheck warnings (Anders Kaseorg) -- Keith Winstein Sun, 10 Mar 2013 17:46:37 -0400 mosh (1.2.3-1) unstable; urgency=low * Version 1.2.3 released. * Update AES-OCB implementation (Keegan McAllister) * More conservative MTU and datagram sizing -- Keith Winstein Fri, 19 Oct 2012 15:21:54 -0400 mosh (1.2.2.95rc1-1) unstable; urgency=low * Security improvements: * Use OpenSSL AES implementation * Don't let bad server dictate IP (Felix Groebert) * New features: * Client hops ports to survive challenging client-side firewall * Server stops sending to save client power (Daniel Drown) * Set DiffServ code point and ECN-capable (Dave Täht) * Slow down if explicit congestion notification received * Warn about unattached Mosh sessions on login * Compatible with KDE konsole (uses BEL to terminate OSC) * Improved heuristic about color of predicted characters * Bug fixes: * Improved performance on systems with expensive time * No longer choke on "ffff::" address for hosts with IPv6 * Platform support: * Build on Solaris and IllumOS (Timo Sirainen, Ira Cooper) * Build on ARM with gcc 4.7 (Alexander Chernyakhovsky) * Licensing changes: * Allow distribution on Apple App Stores * Allow linking with OpenSSL -- Keith Winstein Fri, 05 Oct 2012 19:45:51 -0400 mosh (1.2.2-1) unstable; urgency=low * Version 1.2.2 released. * Remove warning on out-of-order/duplicated datagrams * Add "experimental" prediction mode -- Keith Winstein Tue, 12 Jun 2012 14:52:17 -0400 mosh (1.2.1-1) unstable; urgency=low * Version 1.2.1 released. * Warning on out-of-order/duplicated datagrams (or failed nonce increment) * Clearer error message on invalid port number -- Keith Winstein Fri, 25 May 2012 18:03:31 -0400 mosh (1.2.0.97-1) unstable; urgency=low * Cosmetic cleanups to quit sequence when server is firewalled -- Keith Winstein Wed, 23 May 2012 12:16:47 -0400 mosh (1.2.0.96-1) unstable; urgency=low * Include bash_completion file (ejeffrey) * Include UFW firewall profile (Fumihito YOSHIDA) -- Keith Winstein Wed, 23 May 2012 07:05:54 -0400 mosh (1.2.0.95-1) unstable; urgency=low * Improve performance on lossy links. * New diagnostic message when link is dead in only one direction. * Use less CPU when link is down. (Keegan McAllister) * Use much less CPU when application or mosh-server sends large repeat counts (resolves CVE-2012-2385, reported by Timo Juhani Lindfors). * Use less memory when mosh-server is malicious. * Fix vttest regression re: wrapping and tabs. * Enable roundtrip verifier of terminal emulator correctness when verbose. * Remove skalibs as a dependency. (Keegan McAllister) * Remove use of poll() and OS X poll workaround. (Keegan McAllister) -- Keith Winstein Wed, 23 May 2012 03:36:13 -0400 mosh (1.2-1) unstable; urgency=low * Version 1.2 released. -- Keith Winstein Thu, 26 Apr 2012 01:35:59 -0400 mosh (1.1.99b-1) unstable; urgency=low * Update THANKS and copyright information -- Keith Winstein Wed, 25 Apr 2012 02:53:21 -0400 mosh (1.1.99a-1) unstable; urgency=low * Fix using broken system poll on OS X 10.6 -- Keith Winstein Wed, 25 Apr 2012 02:30:44 -0400 mosh (1.1.99-1) unstable; urgency=low * Fix build problems on FreeBSD 7 and 8 -- Keith Winstein Tue, 24 Apr 2012 23:52:22 -0400 mosh (1.1.98-1) unstable; urgency=low * Fix build problems on Cygwin * Fix build problems on Debian armel/armhf * Fix alignment problems on ARM -- Keith Winstein Tue, 24 Apr 2012 19:00:03 -0400 mosh (1.1.97-1) unstable; urgency=low * Revert use of protobufs' zero-copy gzip stream -- Keith Winstein Mon, 23 Apr 2012 22:53:44 -0400 mosh (1.1.96-1) unstable; urgency=low * Include binary hardening check script in GNU-style tar.gz -- Keith Winstein Mon, 23 Apr 2012 20:21:14 -0400 mosh (1.1.95-1) unstable; urgency=low * Eliminate PPC32 dependence on posix_memalign() -- Keith Winstein Sun, 22 Apr 2012 23:18:13 -0400 mosh (1.1.94e-1) unstable; urgency=low * Improve responsiveness on fast links * Include poll implementation for Mac OS X * Use protobufs' zero-copy gzip stream -- Keith Winstein Sun, 22 Apr 2012 16:59:25 -0400 mosh (1.1.94d-1) unstable; urgency=low * More helpful locale-related error messages * Improve prediction transparency * Honor .hushlogin * Set PWD to deal with home directories that are symlinks * Back off overlay frame rate when all timing tests have fired (saves CPU) * -v flag now makes server more verbose -- Keith Winstein Thu, 19 Apr 2012 02:44:48 -0400 mosh (1.1.94c-1) unstable; urgency=low * Further improved Debian rule to disable conflicting stack protector flags -- Keith Winstein Tue, 17 Apr 2012 04:17:08 -0400 mosh (1.1.94b-1) unstable; urgency=low * Improved Debian rule to disable conflicting stack protector flags -- Keith Winstein Tue, 17 Apr 2012 03:24:23 -0400 mosh (1.1.94a-1) unstable; urgency=low * Bump base version -- Keith Winstein Tue, 17 Apr 2012 02:36:30 -0400 mosh (1.1.94-2) unstable; urgency=low * Disable conflicting Debian/Ubuntu stack protector flags -- Keith Winstein Tue, 17 Apr 2012 02:15:41 -0400 mosh (1.1.94-1) unstable; urgency=low * Release pre-candidate for mosh 1.2 * Remove Boost as a dependency (Keegan McAllister) * More friendly error messages in some common cases * Now passes locale-related env vars over the connection * Remove skalibs as a depenency on Debian/Ubuntu (Keegan McAllister) * Fix startup script to no longer hang on some Macs (Jay Freeman) * Fix argument parsing on FreeBSD (Daniel O'Connor) * Use binary hardening flags where available (Keegan McAllister) * Several improvements to terminal emulation correctness * Now prints message of the day (motd) * Allows core dumps of subsidiary applications if desired * Cleaner execution of subsidiary shell (Jay Freeman) * Fix leaking utmp entries (Ed Schouten) * Add support for Cygwin (Joshua Pollack) * Add support for RHEL/CentOS 5 (Reini Urban) * Use less CPU when server's network is down * Disable posterization of 256colors to 8 ANSI colors -- Keith Winstein Mon, 16 Apr 2012 21:16:28 -0400 mosh (1.1.3a-1) unstable; urgency=low * Support 16-color escape sequences (Anders Kaseorg) * Fix some wraparound glitches * Fix escape sequences when server run on BSD and OS X (Peter Jeremy) * FreeBSD support (Ben Kaduk) * Fixes to build on iOS (Peter Iannucci) * Add some tests (Keegan McAllister) * Support OS X iTerm with lowercase "utf-8" * Avoid unnecessary link with -lncurses when possible (Anders Kaseorg) -- Keith Winstein Wed, 11 Apr 2012 03:36:49 -0400 mosh (1.1.3-1) unstable; urgency=low * Version 1.1.3 released. -- Keith Winstein Tue, 03 Apr 2012 12:33:46 -0400 mosh (1.1.2c-1) unstable; urgency=low * Fix bug causing occasional missing (or spurious) wraparound copy-and-paste -- Keith Winstein Mon, 02 Apr 2012 19:08:19 -0400 mosh (1.1.2b-1) unstable; urgency=low * Close connection after four petabytes (per OCB recommendation) -- Keith Winstein Sat, 31 Mar 2012 17:25:10 -0400 mosh (1.1.2a-1) unstable; urgency=low * Fixes to build on armel and armhf (Keegan McAllister) * Fix to build on FreeBSD, with warning about broken tty multibyte delete (reported by Christoph Egger) * Keep sane values in unused wx_xpixel/wx_ypixel values of struct winsize. -- Keith Winstein Sat, 31 Mar 2012 15:51:31 -0400 mosh (1.1.2-1) unstable; urgency=low * Version 1.1.2 released. -- Keith Winstein Wed, 28 Mar 2012 18:02:28 +0200 mosh (1.1.1a-1) unstable; urgency=low * Fixes to build on OS X 10.5 with older gcc and -lpoll (Quentin Smith) * Add --with-utempter and --without-utempter per gentoo req (Michael Weber) * configure now requires ncurses headers (and others) to be installed * Consolidate locale routines to help Android port (Keegan McAllister) -- Keith Winstein Wed, 28 Mar 2012 15:16:51 +0200 mosh (1.1.1-1) unstable; urgency=low * Version 1.1.1 released. * Fix spec file and build failure on Fedora. * Print out error message properly on fatal_assert(). * Support for machines without posix_memalign(). -- Keith Winstein Tue, 27 Mar 2012 01:18:26 +0200 mosh (1.1-1) unstable; urgency=low * Version 1.1 released -- Keith Winstein Thu, 22 Mar 2012 18:40:59 -0400 mosh (1.0.9d-1) unstable; urgency=low * Fix bug preventing -p argument from working -- Keith Winstein Wed, 21 Mar 2012 22:46:51 -0400 mosh (1.0.9c-1) unstable; urgency=low * Improved building on Mac OS X (Anders Kaseorg) * PRNG (for chaff) now reads from /dev/urandom (Keegan McAllister) * Startup script cleanups and fix shell quoting (Anders Kaseorg) * Disables core dumps (containing session key) (Keegan McAllister) * Improved terminal shutdown and other cosmetic fixes -- Keith Winstein Wed, 21 Mar 2012 19:23:41 -0400 mosh (1.0.9b-1) unstable; urgency=low * More efficient repaint when scrolling top part of window -- Keith Winstein Sun, 18 Mar 2012 16:37:30 -0400 mosh (1.0.9a-1) unstable; urgency=low * Add chaff to datagrams to frustrate statistical analysis of length -- Keith Winstein Sun, 18 Mar 2012 14:00:11 -0400 mosh (1.0.9-1) unstable; urgency=low * Release candidate for version 1.1. * Allows user to specify remote command to execute. * Only advertises 256 colors when user's terminal has 256 colors. -- Keith Winstein Sun, 18 Mar 2012 06:06:43 -0400 mosh (1.0.2-1) unstable; urgency=low * Version 1.02 released. * Uses xterm-256color and supports 256-color escape sequences. * Posterizes to 8 colors unless the user has a 256-color terminal. * Handles terminals without BCE. * Starts login shell. -- Keith Winstein Fri, 16 Mar 2012 17:57:45 -0400 mosh (1.0.1-1) unstable; urgency=low * Version 1.01 released. * Roughly 40% less CPU usage. -- Keith Winstein Wed, 14 Mar 2012 04:55:11 -0400 mosh (1.0-1) unstable; urgency=low * Version 1.0 released. * mosh now supports --version option and prints no-warranty message. -- Keith Winstein Mon, 12 Mar 2012 04:51:43 -0400 mosh (0.98c-1) unstable; urgency=low * Fix pointer comparison (Anders Kaseorg) -- Keith Winstein Sat, 10 Mar 2012 16:50:35 -0500 mosh (0.98b-1) unstable; urgency=low * Update README.md -- Keith Winstein Sat, 10 Mar 2012 14:05:12 -0500 mosh (0.98a-1) unstable; urgency=low * Small cleanup to generated terminal output when predictions active -- Keith Winstein Sat, 10 Mar 2012 06:40:24 -0500 mosh (0.98-1) unstable; urgency=low * Version 0.98 released. * Add option to select server-side UDP port. * Restrict default UDP port range to 60000..61000. * Use TERM / terminfo to decide when to send ECH sequence. * Now works properly inside tmux. -- Keith Winstein Sat, 10 Mar 2012 05:45:34 -0500 mosh (0.97-1) unstable; urgency=low * Merged Mac OS X port (thanks to Quentin Smith and Anders Kaseorg) * Server will quit if no connection within first 60 seconds * Startup script no longer requires threaded Perl * Add --enable-compile-warnings=error to ./configure * Fix some flicker issues with adaptive prediction mode -- Keith Winstein Wed, 07 Mar 2012 04:17:19 -0500 mosh (0.96a-3) unstable; urgency=low * Update copyright file to correct pkg.m4 is GPL-2+ not GPL-2 -- Keith Winstein Sat, 03 Mar 2012 19:06:43 -0500 mosh (0.96a-2) unstable; urgency=low * Update copyright file to include public domain files and other licenses -- Keith Winstein Sat, 03 Mar 2012 14:05:15 -0500 mosh (0.96a-1) unstable; urgency=low * Version 0.96a released. * Fix build problem on i386 -- Keith Winstein Sun, 26 Feb 2012 13:49:44 -0500 mosh (0.96-1) unstable; urgency=low * Version 0.96 released. * Compress all instructions with zlib -- Keith Winstein Sun, 26 Feb 2012 04:40:46 -0500 mosh (0.95-1) unstable; urgency=low * Version 0.95 released. * Make echo acknowledgment reliable to reduce spurious mispredict detections. * Require two dropped heartbeats before showing blue bar of lost contact * Print newline before MOSH CONNECT string to make more robust * Disable ControlMaster in initial SSH connection so proxy is always used * Make retransmissions occur at frame rate for limited time after loss -- Keith Winstein Sat, 25 Feb 2012 15:15:59 -0500 mosh (0.94c-1) unstable; urgency=low * Move to protobuf (from protobuf-lite) to make build on Ubuntu 10.04 -- Keith Winstein Mon, 20 Feb 2012 00:39:12 -0500 mosh (0.94b-1) unstable; urgency=low * Relax autoconf dependency -- Keith Winstein Sun, 19 Feb 2012 22:55:53 -0500 mosh (0.94a-1) unstable; urgency=low * Relax debhelper dependency to make build on Ubuntu 10.04 LTS lucid -- Keith Winstein Sun, 19 Feb 2012 22:03:03 -0500 mosh (0.94-1) unstable; urgency=low * Version 0.94 released. * Make man pages. * Replace C++11 features (lambda and auto) with boost equivalents (Anders Kaseorg) * Now builds with g++ or clang (with libstdc++) -- Keith Winstein Wed, 15 Feb 2012 13:56:11 -0500 mosh (0.93-1) unstable; urgency=low * Version 0.93 released. * Make utmp entries. -- Keith Winstein Mon, 13 Feb 2012 05:44:35 -0500 mosh (0.92-1) unstable; urgency=low * Version 0.92 released. * Allows user to select prediction mode (always/never/adaptive) * Fix bug in server startup on slow hosts * Better prediction when deleting at line ending -- Keith Winstein Mon, 13 Feb 2012 03:53:31 -0500 mosh (0.91a-0ubuntu1) oneiric; urgency=low * Add missing dependencies to debian/control -- Keith Winstein Thu, 09 Feb 2012 09:34:18 -0500 mosh (0.91-0ubuntu1) oneiric; urgency=low * Version 0.91 released. * Startup script support SSH options and aliases (Anders Kaseorg) * End use of Path MTU discovery and allow IP fragmentation because, e.g., Cisco VPN has MTU of 1200 and does not pass ICMP too-big. * Better exception and error handling. * Restrict predictive local echo display to long-delay links (>60 ms RTT) or for temporarily after a "glitch." Otherwise simply show server echos, while maintaining prediction overlay in the background in case it is needed. -- Keith Winstein Thu, 09 Feb 2012 03:05:45 -0500 mosh (0.9b-0ubuntu1) oneiric; urgency=low * Merged Anders Kaseorg fixes to build system -- Keith Winstein Tue, 07 Feb 2012 17:27:42 -0500 mosh (0.9a-0ubuntu1) oneiric; urgency=low * Edit README -- Keith Winstein Sun, 05 Feb 2012 03:50:53 -0500 mosh (0.9-0ubuntu1) oneiric; urgency=low * Initial release -- Keith Winstein Sun, 05 Feb 2012 03:50:53 -0500 mosh-1.2.5/debian/compat000066400000000000000000000000021255051512600151070ustar00rootroot000000000000007 mosh-1.2.5/debian/control000066400000000000000000000016551255051512600153230ustar00rootroot00000000000000Source: mosh Section: net Priority: optional Maintainer: Keith Winstein Build-Depends: debhelper (>= 7.0.50), autotools-dev, protobuf-compiler, libprotobuf-dev, dh-autoreconf, pkg-config, libutempter-dev, zlib1g-dev, libncurses5-dev, libssl-dev, bash-completion Standards-Version: 3.9.6.1 Homepage: http://mosh.mit.edu Vcs-Git: git://github.com/keithw/mosh.git Vcs-Browser: https://github.com/keithw/mosh Package: mosh Architecture: any Pre-Depends: dpkg (>= 1.15.7.2) Depends: ${shlibs:Depends}, ${misc:Depends}, openssh-client Recommends: libio-socket-ip-perl Description: Mobile shell that supports roaming and intelligent local echo Mosh is a remote terminal application that supports: - intermittent network connectivity, - roaming to different IP address without dropping the connection, and - intelligent local echo and line editing to reduce the effects of "network lag" on high-latency connections. mosh-1.2.5/debian/copyright000066400000000000000000000161711255051512600156520ustar00rootroot00000000000000Format: http://www.debian.org/doc/packaging-manuals/copyright-format/1.0/ Upstream-Name: mosh Source: http://github.com/keithw/mosh Files: * Copyright: 2012 Keith Winstein License: GPL-3+ with OpenSSL exception This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. . This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. . You should have received a copy of the GNU General Public License along with this program. If not, see . . In addition, as a special exception, the copyright holders give permission to link the code of portions of this program with the OpenSSL library under certain conditions as described in each individual source file, and distribute linked combinations including the two. . You must obey the GNU General Public License in all respects for all of the code used other than OpenSSL. If you modify file(s) with this exception, you may extend this exception to your version of the file(s), but you are not obligated to do so. If you do not wish to do so, delete this exception statement from your version. If you delete this exception statement from all source files in the program, then also delete it here. . On Debian systems, the complete text of the GNU General Public License version 3 can be found in "/usr/share/common-licenses/GPL-3". Files: m4/pkg.m4 Copyright: 2004 Scott James Remnant License: GPL-2+ This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. . This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. . You should have received a copy of the GNU General Public License along with this program; if not, see . . As a special exception to the GNU General Public License, if you distribute this file as part of a program that contains a configuration script generated by Autoconf, you may include it under the same distribution terms that you use for the rest of that program. . On Debian systems, the complete text of the GNU General Public License version 2 can be found in "/usr/share/common-licenses/GPL-2" Files: m4/ax_check_library.m4 Copyright: 2010 Diego Elio Petteno` License: GPL-3+ This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. . This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. . You should have received a copy of the GNU General Public License along with this program. If not, see . . As a special exception, the respective Autoconf Macro's copyright owner gives unlimited permission to copy, distribute and modify the configure scripts that are the output of Autoconf when processing the Macro. You need not follow the terms of the GNU General Public License when using or distributing such scripts, even though portions of the text of the Macro appear in them. The GNU General Public License (GPL) does govern all other use of the material that constitutes the Autoconf Macro. . This special exception to the GPL applies to versions of the Autoconf Macro released by the Autoconf Archive. When you make and distribute a modified version of the Autoconf Macro, you may extend this special exception to the GPL to apply to your modified version as well. . On Debian systems, the complete text of the GNU General Public License version 3 can be found in "/usr/share/common-licenses/GPL-3". Files: m4/ax_check_compile_flag.m4 m4/ax_check_link_flag.m4 Copyright: 2008 Guido U. Draheim 2011 Maarten Bosmans License: GPL-3+ This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. . This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. . You should have received a copy of the GNU General Public License along with this program. If not, see . . As a special exception, the respective Autoconf Macro's copyright owner gives unlimited permission to copy, distribute and modify the configure scripts that are the output of Autoconf when processing the Macro. You need not follow the terms of the GNU General Public License when using or distributing such scripts, even though portions of the text of the Macro appear in them. The GNU General Public License (GPL) does govern all other use of the material that constitutes the Autoconf Macro. . This special exception to the GPL applies to versions of the Autoconf Macro released by the Autoconf Archive. When you make and distribute a modified version of the Autoconf Macro, you may extend this special exception to the GPL to apply to your modified version as well. Files: src/crypto/ocb.cc Copyright: 2011 Ted Krovetz License: ISC Permission to use, copy, modify, and/or distribute this software for any purpose with or without fee is hereby granted, provided that the above copyright notice and this permission notice appear in all copies. . THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. . Phillip Rogaway holds patents relevant to OCB. See the following for his patent grant: http://www.cs.ucdavis.edu/~rogaway/ocb/grant.htm (also found in ocb-license.html in the Mosh source distribution). Files: src/crypto/ae.h Copyright: none License: public-domain This file is in the public domain. It is provided "as is", without warranty of any kind. Use at your own risk. . Comments are welcome: Ted Krovetz . mosh-1.2.5/debian/docs000066400000000000000000000000121255051512600145550ustar00rootroot00000000000000README.md mosh-1.2.5/debian/mosh.maintscript000066400000000000000000000000671255051512600171410ustar00rootroot00000000000000rm_conffile /etc/bash_completion.d/mosh 1.2.5~ -- "$@" mosh-1.2.5/debian/rules000077500000000000000000000015161255051512600147740ustar00rootroot00000000000000#!/usr/bin/make -f # -*- makefile -*- # Sample debian/rules that uses debhelper. # This file was originally written by Joey Hess and Craig Small. # As a special exception, when this file is copied by dh-make into a # dh-make output file, you may use that output file without restriction. # This special exception was added by Craig Small in version 0.37 of dh-make. # Uncomment this to turn on verbose mode. #export DH_VERBOSE=1 # Through Autoconf we set hardening flags that are actually more aggressive # than the Ubuntu defaults, but they conflict with same. export DEB_BUILD_MAINT_OPTIONS = hardening=-stackprotector -include /usr/share/dpkg/buildflags.mk %: dh $@ --with autoreconf override_dh_auto_configure: dh_auto_configure -- \ --disable-silent-rules \ --enable-ufw \ --enable-completion \ --enable-compile-warnings=error mosh-1.2.5/debian/source/000077500000000000000000000000001255051512600152115ustar00rootroot00000000000000mosh-1.2.5/debian/source/format000066400000000000000000000000141255051512600164170ustar00rootroot000000000000003.0 (quilt) mosh-1.2.5/debian/watch000066400000000000000000000001131255051512600147350ustar00rootroot00000000000000version=3 https://github.com/keithw/mosh/tags .*/mosh-(\d[\d\.]+)\.tar\.gz mosh-1.2.5/fedora/000077500000000000000000000000001255051512600137275ustar00rootroot00000000000000mosh-1.2.5/fedora/mosh.spec000066400000000000000000000051321255051512600155520ustar00rootroot00000000000000Name: mosh Version: 1.2.5 Release: 1%{?dist} Summary: Mobile shell that supports roaming and intelligent local echo License: GPLv3+ Group: Applications/Internet URL: http://mosh.mit.edu/ Source0: https://github.com/downloads/keithw/mosh/mosh-%{version}.tar.gz BuildRequires: protobuf-compiler BuildRequires: protobuf-devel BuildRequires: libutempter-devel BuildRequires: zlib-devel BuildRequires: ncurses-devel BuildRequires: openssl-devel Requires: openssh-clients Requires: openssl Requires: perl-IO-Socket-IP %description Mosh is a remote terminal application that supports: - intermittent network connectivity, - roaming to different IP address without dropping the connection, and - intelligent local echo and line editing to reduce the effects of "network lag" on high-latency connections. %prep %setup -q %build # Use upstream's more aggressive hardening instead of Fedora's defaults export CFLAGS="-g -O2" CXXFLAGS="-g -O2" %configure --enable-compile-warnings=error make %{?_smp_mflags} %install make install DESTDIR=$RPM_BUILD_ROOT %files %doc README.md COPYING ChangeLog %{_bindir}/mosh %{_bindir}/mosh-client %{_bindir}/mosh-server %{_mandir}/man1/mosh.1.gz %{_mandir}/man1/mosh-client.1.gz %{_mandir}/man1/mosh-server.1.gz %changelog * Sun Jul 12 2015 John Hood - 1.2.5-1 - Update to mosh 1.2.5 * Fri Jun 26 2015 John Hood - 1.2.4.95rc2-1 - Update to mosh 1.2.4.95rc2 * Mon Jun 08 2015 John Hood - 1.2.4.95rc1-1 - Update to mosh 1.2.4.95rc1 * Wed Mar 27 2013 Alexander Chernyakhovsky - 1.2.4-1 - Update to mosh 1.2.4 * Sun Mar 10 2013 Alexander Chernyakhovsky - 1.2.3-3 - Rebuilt for Protobuf API change from 2.4.1 to 2.5.0 * Thu Feb 14 2013 Fedora Release Engineering - 1.2.3-2 - Rebuilt for https://fedoraproject.org/wiki/Fedora_19_Mass_Rebuild * Fri Oct 19 2012 Alexander Chernyakhovsky - 1.2.3-1 - Update to mosh 1.2.3 * Fri Jul 20 2012 Fedora Release Engineering - 1.2.2-2 - Rebuilt for https://fedoraproject.org/wiki/Fedora_18_Mass_Rebuild * Wed Jun 13 2012 Alexander Chernyakhovsky - 1.2.2-1 - Update to mosh 1.2.2 * Sat Apr 28 2012 Alexander Chernyakhovsky - 1.2-2 - Add -g and -O2 CFLAGS * Fri Apr 27 2012 Alexander Chernyakhovsky - 1.2-1 - Update to mosh 1.2. * Mon Mar 26 2012 Alexander Chernyakhovsky - 1.1.1-1 - Update to mosh 1.1.1. * Wed Mar 21 2012 Alexander Chernyakhovsky - 1.1-1 - Initial packaging for mosh. mosh-1.2.5/m4/000077500000000000000000000000001255051512600130075ustar00rootroot00000000000000mosh-1.2.5/m4/ax_check_compile_flag.m4000066400000000000000000000062511255051512600175230ustar00rootroot00000000000000# =========================================================================== # http://www.gnu.org/software/autoconf-archive/ax_check_compile_flag.html # =========================================================================== # # SYNOPSIS # # AX_CHECK_COMPILE_FLAG(FLAG, [ACTION-SUCCESS], [ACTION-FAILURE], [EXTRA-FLAGS]) # # DESCRIPTION # # Check whether the given FLAG works with the current language's compiler # or gives an error. (Warnings, however, are ignored) # # ACTION-SUCCESS/ACTION-FAILURE are shell commands to execute on # success/failure. # # If EXTRA-FLAGS is defined, it is added to the current language's default # flags (e.g. CFLAGS) when the check is done. The check is thus made with # the flags: "CFLAGS EXTRA-FLAGS FLAG". This can for example be used to # force the compiler to issue an error when a bad flag is given. # # NOTE: Implementation based on AX_CFLAGS_GCC_OPTION. Please keep this # macro in sync with AX_CHECK_{PREPROC,LINK}_FLAG. # # LICENSE # # Copyright (c) 2008 Guido U. Draheim # Copyright (c) 2011 Maarten Bosmans # # This program is free software: you can redistribute it and/or modify it # under the terms of the GNU General Public License as published by the # Free Software Foundation, either version 3 of the License, or (at your # option) any later version. # # This program is distributed in the hope that it will be useful, but # WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General # Public License for more details. # # You should have received a copy of the GNU General Public License along # with this program. If not, see . # # As a special exception, the respective Autoconf Macro's copyright owner # gives unlimited permission to copy, distribute and modify the configure # scripts that are the output of Autoconf when processing the Macro. You # need not follow the terms of the GNU General Public License when using # or distributing such scripts, even though portions of the text of the # Macro appear in them. The GNU General Public License (GPL) does govern # all other use of the material that constitutes the Autoconf Macro. # # This special exception to the GPL applies to versions of the Autoconf # Macro released by the Autoconf Archive. When you make and distribute a # modified version of the Autoconf Macro, you may extend this special # exception to the GPL to apply to your modified version as well. #serial 2 AC_DEFUN([AX_CHECK_COMPILE_FLAG], [AC_PREREQ(2.59)dnl for _AC_LANG_PREFIX AS_VAR_PUSHDEF([CACHEVAR],[ax_cv_check_[]_AC_LANG_ABBREV[]flags_$4_$1])dnl AC_CACHE_CHECK([whether _AC_LANG compiler accepts $1], CACHEVAR, [ ax_check_save_flags=$[]_AC_LANG_PREFIX[]FLAGS _AC_LANG_PREFIX[]FLAGS="$[]_AC_LANG_PREFIX[]FLAGS $4 $1" AC_COMPILE_IFELSE([AC_LANG_PROGRAM()], [AS_VAR_SET(CACHEVAR,[yes])], [AS_VAR_SET(CACHEVAR,[no])]) _AC_LANG_PREFIX[]FLAGS=$ax_check_save_flags]) AS_IF([test x"AS_VAR_GET(CACHEVAR)" = xyes], [m4_default([$2], :)], [m4_default([$3], :)]) AS_VAR_POPDEF([CACHEVAR])dnl ])dnl AX_CHECK_COMPILE_FLAGS mosh-1.2.5/m4/ax_check_library.m4000066400000000000000000000073211255051512600165450ustar00rootroot00000000000000# =========================================================================== # http://www.gnu.org/software/autoconf-archive/ax_check_library.html # =========================================================================== # # SYNOPSIS # # AX_CHECK_LIBRARY(VARIABLE-PREFIX, HEADER-FILE, LIBRARY-FILE, # [ACTION-IF-FOUND], [ACTION-IF-NOT_FOUND]) # # DESCRIPTION # # Provides a generic test for a given library, similar in concept to the # PKG_CHECK_MODULES macro used by pkg-config. # # Most simplest libraries can be checked against simply through the # presence of a header file and a library to link to. This macro allows to # wrap around the test so that it doesn't have to be recreated each time. # # Rather than define --with-$LIBRARY arguments, it uses variables in the # same way that PKG_CHECK_MODULES does. It doesn't, though, use the same # names, since you shouldn't provide a value for LIBS or CFLAGS but rather # for LDFLAGS and CPPFLAGS, to tell the linker and compiler where to find # libraries and headers respectively. # # If the library is find, HAVE_PREFIX is defined, and in all cases # PREFIX_LDFLAGS and PREFIX_CPPFLAGS are substituted. # # Example: # # AX_CHECK_LIBRARY([LIBEVENT], [event.h], [event], [], # [AC_MSG_ERROR([Unable to find libevent])]) # # LICENSE # # Copyright (c) 2010 Diego Elio Petteno` # # This program is free software: you can redistribute it and/or modify it # under the terms of the GNU General Public License as published by the # Free Software Foundation, either version 3 of the License, or (at your # option) any later version. # # This program is distributed in the hope that it will be useful, but # WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General # Public License for more details. # # You should have received a copy of the GNU General Public License along # with this program. If not, see . # # As a special exception, the respective Autoconf Macro's copyright owner # gives unlimited permission to copy, distribute and modify the configure # scripts that are the output of Autoconf when processing the Macro. You # need not follow the terms of the GNU General Public License when using # or distributing such scripts, even though portions of the text of the # Macro appear in them. The GNU General Public License (GPL) does govern # all other use of the material that constitutes the Autoconf Macro. # # This special exception to the GPL applies to versions of the Autoconf # Macro released by the Autoconf Archive. When you make and distribute a # modified version of the Autoconf Macro, you may extend this special # exception to the GPL to apply to your modified version as well. #serial 4 AC_DEFUN([AX_CHECK_LIBRARY], [ AC_ARG_VAR($1[_CPPFLAGS], [C preprocessor flags for ]$1[ headers]) AC_ARG_VAR($1[_LDFLAGS], [linker flags for ]$1[ libraries]) AC_CACHE_VAL(AS_TR_SH([ax_cv_have_]$1), [save_CPPFLAGS="$CPPFLAGS" save_LDFLAGS="$LDFLAGS" save_LIBS="$LIBS" AS_IF([test "x$]$1[_CPPFLAGS" != "x"], [CPPFLAGS="$CPPFLAGS $]$1[_CPPFLAGS"]) AS_IF([test "x$]$1[_LDFLAGS" != "x"], [LDFLAGS="$LDFLAGS $]$1[_LDFLAGS"]) AC_CHECK_HEADER($2, [ AC_CHECK_LIB($3, [main], [AS_TR_SH([ax_cv_have_]$1)=yes], [AS_TR_SH([ax_cv_have_]$1)=no]) ], [AS_TR_SH([ax_cv_have_]$1)=no]) CPPFLAGS="$save_CPPFLAGS" LDFLAGS="$save_LDFLAGS" LIBS="$save_LIBS" ]) AS_IF([test "$]AS_TR_SH([ax_cv_have_]$1)[" = "yes"], AC_DEFINE([HAVE_]$1, [1], [Define to 1 if ]$1[ is found]) [$4], [$5]) ]) mosh-1.2.5/m4/ax_check_link_flag.m4000066400000000000000000000057601255051512600170340ustar00rootroot00000000000000# =========================================================================== # http://www.gnu.org/software/autoconf-archive/ax_check_link_flag.html # =========================================================================== # # SYNOPSIS # # AX_CHECK_LINK_FLAG(FLAG, [ACTION-SUCCESS], [ACTION-FAILURE], [EXTRA-FLAGS]) # # DESCRIPTION # # Check whether the given FLAG works with the linker or gives an error. # (Warnings, however, are ignored) # # ACTION-SUCCESS/ACTION-FAILURE are shell commands to execute on # success/failure. # # If EXTRA-FLAGS is defined, it is added to the linker's default flags # when the check is done. The check is thus made with the flags: "LDFLAGS # EXTRA-FLAGS FLAG". This can for example be used to force the linker to # issue an error when a bad flag is given. # # NOTE: Implementation based on AX_CFLAGS_GCC_OPTION. Please keep this # macro in sync with AX_CHECK_{PREPROC,COMPILE}_FLAG. # # LICENSE # # Copyright (c) 2008 Guido U. Draheim # Copyright (c) 2011 Maarten Bosmans # # This program is free software: you can redistribute it and/or modify it # under the terms of the GNU General Public License as published by the # Free Software Foundation, either version 3 of the License, or (at your # option) any later version. # # This program is distributed in the hope that it will be useful, but # WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General # Public License for more details. # # You should have received a copy of the GNU General Public License along # with this program. If not, see . # # As a special exception, the respective Autoconf Macro's copyright owner # gives unlimited permission to copy, distribute and modify the configure # scripts that are the output of Autoconf when processing the Macro. You # need not follow the terms of the GNU General Public License when using # or distributing such scripts, even though portions of the text of the # Macro appear in them. The GNU General Public License (GPL) does govern # all other use of the material that constitutes the Autoconf Macro. # # This special exception to the GPL applies to versions of the Autoconf # Macro released by the Autoconf Archive. When you make and distribute a # modified version of the Autoconf Macro, you may extend this special # exception to the GPL to apply to your modified version as well. #serial 2 AC_DEFUN([AX_CHECK_LINK_FLAG], [AS_VAR_PUSHDEF([CACHEVAR],[ax_cv_check_ldflags_$4_$1])dnl AC_CACHE_CHECK([whether the linker accepts $1], CACHEVAR, [ ax_check_save_flags=$LDFLAGS LDFLAGS="$LDFLAGS $4 $1" AC_LINK_IFELSE([AC_LANG_PROGRAM()], [AS_VAR_SET(CACHEVAR,[yes])], [AS_VAR_SET(CACHEVAR,[no])]) LDFLAGS=$ax_check_save_flags]) AS_IF([test x"AS_VAR_GET(CACHEVAR)" = xyes], [m4_default([$2], :)], [m4_default([$3], :)]) AS_VAR_POPDEF([CACHEVAR])dnl ])dnl AX_CHECK_LINK_FLAGS mosh-1.2.5/m4/ax_with_curses.m4000066400000000000000000000607331255051512600163110ustar00rootroot00000000000000# =========================================================================== # http://www.gnu.org/software/autoconf-archive/ax_with_curses.html # =========================================================================== # # SYNOPSIS # # AX_WITH_CURSES # # DESCRIPTION # # This macro checks whether a SysV or X/Open-compatible Curses library is # present, along with the associated header file. The NcursesW # (wide-character) library is searched for first, followed by Ncurses, # then the system-default plain Curses. The first library found is the # one returned. # # The following options are understood: --with-ncursesw, --with-ncurses, # --without-ncursesw, --without-ncurses. The "--with" options force the # macro to use that particular library, terminating with an error if not # found. The "--without" options simply skip the check for that library. # The effect on the search pattern is: # # (no options) - NcursesW, Ncurses, Curses # --with-ncurses --with-ncursesw - NcursesW only [*] # --without-ncurses --with-ncursesw - NcursesW only [*] # --with-ncursesw - NcursesW only [*] # --with-ncurses --without-ncursesw - Ncurses only [*] # --with-ncurses - NcursesW, Ncurses [**] # --without-ncurses --without-ncursesw - Curses only # --without-ncursesw - Ncurses, Curses # --without-ncurses - NcursesW, Curses # # [*] If the library is not found, abort the configure script. # # [**] If the second library (Ncurses) is not found, abort configure. # # The following preprocessor symbols may be defined by this macro if the # appropriate conditions are met: # # HAVE_CURSES - if any SysV or X/Open Curses library found # HAVE_CURSES_ENHANCED - if library supports X/Open Enhanced functions # HAVE_CURSES_COLOR - if library supports color (enhanced functions) # HAVE_CURSES_OBSOLETE - if library supports certain obsolete features # HAVE_NCURSESW - if NcursesW (wide char) library is to be used # HAVE_NCURSES - if the Ncurses library is to be used # # HAVE_CURSES_H - if is present and should be used # HAVE_NCURSESW_H - if should be used # HAVE_NCURSES_H - if should be used # HAVE_NCURSESW_CURSES_H - if should be used # HAVE_NCURSES_CURSES_H - if should be used # # (These preprocessor symbols are discussed later in this document.) # # The following output variable is defined by this macro; it is precious # and may be overridden on the ./configure command line: # # CURSES_LIB - library to add to xxx_LDADD # # The library listed in CURSES_LIB is NOT added to LIBS by default. You # need to add CURSES_LIB to the appropriate xxx_LDADD line in your # Makefile.am. For example: # # prog_LDADD = @CURSES_LIB@ # # If CURSES_LIB is set on the configure command line (such as by running # "./configure CURSES_LIB=-lmycurses"), then the only header searched for # is . The user may use the CPPFLAGS precious variable to # override the standard #include search path. If the user needs to # specify an alternative path for a library (such as for a non-standard # NcurseW), the user should use the LDFLAGS variable. # # The following shell variables may be defined by this macro: # # ax_cv_curses - set to "yes" if any Curses library found # ax_cv_curses_enhanced - set to "yes" if Enhanced functions present # ax_cv_curses_color - set to "yes" if color functions present # ax_cv_curses_obsolete - set to "yes" if obsolete features present # # ax_cv_ncursesw - set to "yes" if NcursesW library found # ax_cv_ncurses - set to "yes" if Ncurses library found # ax_cv_plaincurses - set to "yes" if plain Curses library found # ax_cv_curses_which - set to "ncursesw", "ncurses", "plaincurses" or "no" # # These variables can be used in your configure.ac to determine the level # of support you need from the Curses library. For example, if you must # have either Ncurses or NcursesW, you could include: # # AX_WITH_CURSES # if test "x$ax_cv_ncursesw" != xyes && test "x$ax_cv_ncurses" != xyes; then # AX_MSG_ERROR([requires either NcursesW or Ncurses library]) # fi # # If any Curses library will do (but one must be present and must support # color), you could use: # # AX_WITH_CURSES # if test "x$ax_cv_curses" != xyes || test "x$ax_cv_curses_color" != xyes; then # AC_MSG_ERROR([requires an X/Open-compatible Curses library with color]) # fi # # Certain preprocessor symbols and shell variables defined by this macro # can be used to determine various features of the Curses library. In # particular, HAVE_CURSES and ax_cv_curses are defined if the Curses # library found conforms to the traditional SysV and/or X/Open Base Curses # definition. Any working Curses library conforms to this level. # # HAVE_CURSES_ENHANCED and ax_cv_curses_enhanced are defined if the # library supports the X/Open Enhanced Curses definition. In particular, # the wide-character types attr_t, cchar_t and wint_t, the functions # wattr_set() and wget_wch() and the macros WA_NORMAL and _XOPEN_CURSES # are checked. The Ncurses library does NOT conform to this definition, # although NcursesW does. # # HAVE_CURSES_COLOR and ax_cv_curses_color are defined if the library # supports color functions and macros such as COLOR_PAIR, A_COLOR, # COLOR_WHITE, COLOR_RED and init_pair(). These are NOT part of the # X/Open Base Curses definition, but are part of the Enhanced set of # functions. The Ncurses library DOES support these functions, as does # NcursesW. # # HAVE_CURSES_OBSOLETE and ax_cv_curses_obsolete are defined if the # library supports certain features present in SysV and BSD Curses but not # defined in the X/Open definition. In particular, the functions # getattrs(), getcurx() and getmaxx() are checked. # # To use the HAVE_xxx_H preprocessor symbols, insert the following into # your system.h (or equivalent) header file: # # #if defined HAVE_NCURSESW_CURSES_H # # include # #elif defined HAVE_NCURSESW_H # # include # #elif defined HAVE_NCURSES_CURSES_H # # include # #elif defined HAVE_NCURSES_H # # include # #elif defined HAVE_CURSES_H # # include # #else # # error "SysV or X/Open-compatible Curses header file required" # #endif # # For previous users of this macro: you should not need to change anything # in your configure.ac or Makefile.am, as the previous (serial 10) # semantics are still valid. However, you should update your system.h (or # equivalent) header file to the fragment shown above. You are encouraged # also to make use of the extended functionality provided by this version # of AX_WITH_CURSES, as well as in the additional macros # AX_WITH_CURSES_PANEL, AX_WITH_CURSES_MENU and AX_WITH_CURSES_FORM. # # LICENSE # # Copyright (c) 2009 Mark Pulford # Copyright (c) 2009 Damian Pietras # Copyright (c) 2012 Reuben Thomas # Copyright (c) 2011 John Zaitseff # # This program is free software: you can redistribute it and/or modify it # under the terms of the GNU General Public License as published by the # Free Software Foundation, either version 3 of the License, or (at your # option) any later version. # # This program is distributed in the hope that it will be useful, but # WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General # Public License for more details. # # You should have received a copy of the GNU General Public License along # with this program. If not, see . # # As a special exception, the respective Autoconf Macro's copyright owner # gives unlimited permission to copy, distribute and modify the configure # scripts that are the output of Autoconf when processing the Macro. You # need not follow the terms of the GNU General Public License when using # or distributing such scripts, even though portions of the text of the # Macro appear in them. The GNU General Public License (GPL) does govern # all other use of the material that constitutes the Autoconf Macro. # # This special exception to the GPL applies to versions of the Autoconf # Macro released by the Autoconf Archive. When you make and distribute a # modified version of the Autoconf Macro, you may extend this special # exception to the GPL to apply to your modified version as well. #serial 13 AU_ALIAS([MP_WITH_CURSES], [AX_WITH_CURSES]) AC_DEFUN([AX_WITH_CURSES], [ AC_ARG_VAR([CURSES_LIB], [linker library for Curses, e.g. -lcurses]) AC_ARG_WITH([ncurses], [AS_HELP_STRING([--with-ncurses], [force the use of Ncurses or NcursesW])], [], [with_ncurses=check]) AC_ARG_WITH([ncursesw], [AS_HELP_STRING([--without-ncursesw], [do not use NcursesW (wide character support)])], [], [with_ncursesw=check]) ax_saved_LIBS=$LIBS AS_IF([test "x$with_ncurses" = xyes || test "x$with_ncursesw" = xyes], [ax_with_plaincurses=no], [ax_with_plaincurses=check]) ax_cv_curses_which=no # Test for NcursesW AS_IF([test "x$CURSES_LIB" = x && test "x$with_ncursesw" != xno], [ LIBS="$ax_saved_LIBS -lncursesw" AC_CACHE_CHECK([for NcursesW wide-character library], [ax_cv_ncursesw], [ AC_LINK_IFELSE([AC_LANG_CALL([], [initscr])], [ax_cv_ncursesw=yes], [ax_cv_ncursesw=no]) ]) AS_IF([test "x$ax_cv_ncursesw" = xno && test "x$with_ncursesw" = xyes], [ AC_MSG_ERROR([--with-ncursesw specified but could not find NcursesW library]) ]) AS_IF([test "x$ax_cv_ncursesw" = xyes], [ ax_cv_curses=yes ax_cv_curses_which=ncursesw CURSES_LIB="-lncursesw" AC_DEFINE([HAVE_NCURSESW], [1], [Define to 1 if the NcursesW library is present]) AC_DEFINE([HAVE_CURSES], [1], [Define to 1 if a SysV or X/Open compatible Curses library is present]) AC_CACHE_CHECK([for working ncursesw/curses.h], [ax_cv_header_ncursesw_curses_h], [ AC_LINK_IFELSE([AC_LANG_PROGRAM([[ @%:@define _XOPEN_SOURCE_EXTENDED 1 @%:@include ]], [[ chtype a = A_BOLD; int b = KEY_LEFT; chtype c = COLOR_PAIR(1) & A_COLOR; attr_t d = WA_NORMAL; cchar_t e; wint_t f; int g = getattrs(stdscr); int h = getcurx(stdscr) + getmaxx(stdscr); initscr(); init_pair(1, COLOR_WHITE, COLOR_RED); wattr_set(stdscr, d, 0, NULL); wget_wch(stdscr, &f); ]])], [ax_cv_header_ncursesw_curses_h=yes], [ax_cv_header_ncursesw_curses_h=no]) ]) AS_IF([test "x$ax_cv_header_ncursesw_curses_h" = xyes], [ ax_cv_curses_enhanced=yes ax_cv_curses_color=yes ax_cv_curses_obsolete=yes AC_DEFINE([HAVE_CURSES_ENHANCED], [1], [Define to 1 if library supports X/Open Enhanced functions]) AC_DEFINE([HAVE_CURSES_COLOR], [1], [Define to 1 if library supports color (enhanced functions)]) AC_DEFINE([HAVE_CURSES_OBSOLETE], [1], [Define to 1 if library supports certain obsolete features]) AC_DEFINE([HAVE_NCURSESW_CURSES_H], [1], [Define to 1 if is present]) ]) AC_CACHE_CHECK([for working ncursesw.h], [ax_cv_header_ncursesw_h], [ AC_LINK_IFELSE([AC_LANG_PROGRAM([[ @%:@define _XOPEN_SOURCE_EXTENDED 1 @%:@include ]], [[ chtype a = A_BOLD; int b = KEY_LEFT; chtype c = COLOR_PAIR(1) & A_COLOR; attr_t d = WA_NORMAL; cchar_t e; wint_t f; int g = getattrs(stdscr); int h = getcurx(stdscr) + getmaxx(stdscr); initscr(); init_pair(1, COLOR_WHITE, COLOR_RED); wattr_set(stdscr, d, 0, NULL); wget_wch(stdscr, &f); ]])], [ax_cv_header_ncursesw_h=yes], [ax_cv_header_ncursesw_h=no]) ]) AS_IF([test "x$ax_cv_header_ncursesw_h" = xyes], [ ax_cv_curses_enhanced=yes ax_cv_curses_color=yes ax_cv_curses_obsolete=yes AC_DEFINE([HAVE_CURSES_ENHANCED], [1], [Define to 1 if library supports X/Open Enhanced functions]) AC_DEFINE([HAVE_CURSES_COLOR], [1], [Define to 1 if library supports color (enhanced functions)]) AC_DEFINE([HAVE_CURSES_OBSOLETE], [1], [Define to 1 if library supports certain obsolete features]) AC_DEFINE([HAVE_NCURSESW_H], [1], [Define to 1 if is present]) ]) AC_CACHE_CHECK([for working ncurses.h], [ax_cv_header_ncurses_h_with_ncursesw], [ AC_LINK_IFELSE([AC_LANG_PROGRAM([[ @%:@define _XOPEN_SOURCE_EXTENDED 1 @%:@include ]], [[ chtype a = A_BOLD; int b = KEY_LEFT; chtype c = COLOR_PAIR(1) & A_COLOR; attr_t d = WA_NORMAL; cchar_t e; wint_t f; int g = getattrs(stdscr); int h = getcurx(stdscr) + getmaxx(stdscr); initscr(); init_pair(1, COLOR_WHITE, COLOR_RED); wattr_set(stdscr, d, 0, NULL); wget_wch(stdscr, &f); ]])], [ax_cv_header_ncurses_h_with_ncursesw=yes], [ax_cv_header_ncurses_h_with_ncursesw=no]) ]) AS_IF([test "x$ax_cv_header_ncurses_h_with_ncursesw" = xyes], [ ax_cv_curses_enhanced=yes ax_cv_curses_color=yes ax_cv_curses_obsolete=yes AC_DEFINE([HAVE_CURSES_ENHANCED], [1], [Define to 1 if library supports X/Open Enhanced functions]) AC_DEFINE([HAVE_CURSES_COLOR], [1], [Define to 1 if library supports color (enhanced functions)]) AC_DEFINE([HAVE_CURSES_OBSOLETE], [1], [Define to 1 if library supports certain obsolete features]) AC_DEFINE([HAVE_NCURSES_H], [1], [Define to 1 if is present]) ]) AS_IF([test "x$ax_cv_header_ncursesw_curses_h" = xno && test "x$ax_cv_header_ncursesw_h" = xno && test "x$ax_cv_header_ncurses_h_with_ncursesw" = xno], [ AC_MSG_WARN([could not find a working ncursesw/curses.h, ncursesw.h or ncurses.h]) ]) ]) ]) # Test for Ncurses AS_IF([test "x$CURSES_LIB" = x && test "x$with_ncurses" != xno && test "x$ax_cv_curses_which" = xno], [ LIBS="$ax_saved_LIBS -lncurses" AC_CACHE_CHECK([for Ncurses library], [ax_cv_ncurses], [ AC_LINK_IFELSE([AC_LANG_CALL([], [initscr])], [ax_cv_ncurses=yes], [ax_cv_ncurses=no]) ]) AS_IF([test "x$ax_cv_ncurses" = xno && test "x$with_ncurses" = xyes], [ AC_MSG_ERROR([--with-ncurses specified but could not find Ncurses library]) ]) AS_IF([test "x$ax_cv_ncurses" = xyes], [ ax_cv_curses=yes ax_cv_curses_which=ncurses CURSES_LIB="-lncurses" AC_DEFINE([HAVE_NCURSES], [1], [Define to 1 if the Ncurses library is present]) AC_DEFINE([HAVE_CURSES], [1], [Define to 1 if a SysV or X/Open compatible Curses library is present]) AC_CACHE_CHECK([for working ncurses/curses.h], [ax_cv_header_ncurses_curses_h], [ AC_LINK_IFELSE([AC_LANG_PROGRAM([[ @%:@include ]], [[ chtype a = A_BOLD; int b = KEY_LEFT; chtype c = COLOR_PAIR(1) & A_COLOR; int g = getattrs(stdscr); int h = getcurx(stdscr) + getmaxx(stdscr); initscr(); init_pair(1, COLOR_WHITE, COLOR_RED); ]])], [ax_cv_header_ncurses_curses_h=yes], [ax_cv_header_ncurses_curses_h=no]) ]) AS_IF([test "x$ax_cv_header_ncurses_curses_h" = xyes], [ ax_cv_curses_color=yes ax_cv_curses_obsolete=yes AC_DEFINE([HAVE_CURSES_COLOR], [1], [Define to 1 if library supports color (enhanced functions)]) AC_DEFINE([HAVE_CURSES_OBSOLETE], [1], [Define to 1 if library supports certain obsolete features]) AC_DEFINE([HAVE_NCURSES_CURSES_H], [1], [Define to 1 if is present]) ]) AC_CACHE_CHECK([for working ncurses.h], [ax_cv_header_ncurses_h], [ AC_LINK_IFELSE([AC_LANG_PROGRAM([[ @%:@include ]], [[ chtype a = A_BOLD; int b = KEY_LEFT; chtype c = COLOR_PAIR(1) & A_COLOR; int g = getattrs(stdscr); int h = getcurx(stdscr) + getmaxx(stdscr); initscr(); init_pair(1, COLOR_WHITE, COLOR_RED); ]])], [ax_cv_header_ncurses_h=yes], [ax_cv_header_ncurses_h=no]) ]) AS_IF([test "x$ax_cv_header_ncurses_h" = xyes], [ ax_cv_curses_color=yes ax_cv_curses_obsolete=yes AC_DEFINE([HAVE_CURSES_COLOR], [1], [Define to 1 if library supports color (enhanced functions)]) AC_DEFINE([HAVE_CURSES_OBSOLETE], [1], [Define to 1 if library supports certain obsolete features]) AC_DEFINE([HAVE_NCURSES_H], [1], [Define to 1 if is present]) ]) AS_IF([test "x$ax_cv_header_ncurses_curses_h" = xno && test "x$ax_cv_header_ncurses_h" = xno], [ AC_MSG_WARN([could not find a working ncurses/curses.h or ncurses.h]) ]) ]) ]) # Test for plain Curses (or if CURSES_LIB was set by user) AS_IF([test "x$with_plaincurses" != xno && test "x$ax_cv_curses_which" = xno], [ AS_IF([test "x$CURSES_LIB" != x], [ LIBS="$ax_saved_LIBS $CURSES_LIB" ], [ LIBS="$ax_saved_LIBS -lcurses" ]) AC_CACHE_CHECK([for Curses library], [ax_cv_plaincurses], [ AC_LINK_IFELSE([AC_LANG_CALL([], [initscr])], [ax_cv_plaincurses=yes], [ax_cv_plaincurses=no]) ]) AS_IF([test "x$ax_cv_plaincurses" = xyes], [ ax_cv_curses=yes ax_cv_curses_which=plaincurses AS_IF([test "x$CURSES_LIB" = x], [ CURSES_LIB="-lcurses" ]) AC_DEFINE([HAVE_CURSES], [1], [Define to 1 if a SysV or X/Open compatible Curses library is present]) # Check for base conformance (and header file) AC_CACHE_CHECK([for working curses.h], [ax_cv_header_curses_h], [ AC_LINK_IFELSE([AC_LANG_PROGRAM([[ @%:@include ]], [[ chtype a = A_BOLD; int b = KEY_LEFT; initscr(); ]])], [ax_cv_header_curses_h=yes], [ax_cv_header_curses_h=no]) ]) AS_IF([test "x$ax_cv_header_curses_h" = xyes], [ AC_DEFINE([HAVE_CURSES_H], [1], [Define to 1 if is present]) # Check for X/Open Enhanced conformance AC_CACHE_CHECK([for X/Open Enhanced Curses conformance], [ax_cv_plaincurses_enhanced], [ AC_LINK_IFELSE([AC_LANG_PROGRAM([[ @%:@define _XOPEN_SOURCE_EXTENDED 1 @%:@include @%:@ifndef _XOPEN_CURSES @%:@error "this Curses library is not enhanced" "this Curses library is not enhanced" @%:@endif ]], [[ chtype a = A_BOLD; int b = KEY_LEFT; chtype c = COLOR_PAIR(1) & A_COLOR; attr_t d = WA_NORMAL; cchar_t e; wint_t f; initscr(); init_pair(1, COLOR_WHITE, COLOR_RED); wattr_set(stdscr, d, 0, NULL); wget_wch(stdscr, &f); ]])], [ax_cv_plaincurses_enhanced=yes], [ax_cv_plaincurses_enhanced=no]) ]) AS_IF([test "x$ax_cv_plaincurses_enhanced" = xyes], [ ax_cv_curses_enhanced=yes ax_cv_curses_color=yes AC_DEFINE([HAVE_CURSES_ENHANCED], [1], [Define to 1 if library supports X/Open Enhanced functions]) AC_DEFINE([HAVE_CURSES_COLOR], [1], [Define to 1 if library supports color (enhanced functions)]) ]) # Check for color functions AC_CACHE_CHECK([for Curses color functions], [ax_cv_plaincurses_color], [ AC_LINK_IFELSE([AC_LANG_PROGRAM([[ @%:@define _XOPEN_SOURCE_EXTENDED 1 @%:@include ]], [[ chtype a = A_BOLD; int b = KEY_LEFT; chtype c = COLOR_PAIR(1) & A_COLOR; initscr(); init_pair(1, COLOR_WHITE, COLOR_RED); ]])], [ax_cv_plaincurses_color=yes], [ax_cv_plaincurses_color=no]) ]) AS_IF([test "x$ax_cv_plaincurses_color" = xyes], [ ax_cv_curses_color=yes AC_DEFINE([HAVE_CURSES_COLOR], [1], [Define to 1 if library supports color (enhanced functions)]) ]) # Check for obsolete functions AC_CACHE_CHECK([for obsolete Curses functions], [ax_cv_plaincurses_obsolete], [ AC_LINK_IFELSE([AC_LANG_PROGRAM([[ @%:@include ]], [[ chtype a = A_BOLD; int b = KEY_LEFT; int g = getattrs(stdscr); int h = getcurx(stdscr) + getmaxx(stdscr); initscr(); ]])], [ax_cv_plaincurses_obsolete=yes], [ax_cv_plaincurses_obsolete=no]) ]) AS_IF([test "x$ax_cv_plaincurses_obsolete" = xyes], [ ax_cv_curses_obsolete=yes AC_DEFINE([HAVE_CURSES_OBSOLETE], [1], [Define to 1 if library supports certain obsolete features]) ]) ]) AS_IF([test "x$ax_cv_header_curses_h" = xno], [ AC_MSG_WARN([could not find a working curses.h]) ]) ]) ]) AS_IF([test "x$ax_cv_curses" != xyes], [ax_cv_curses=no]) AS_IF([test "x$ax_cv_curses_enhanced" != xyes], [ax_cv_curses_enhanced=no]) AS_IF([test "x$ax_cv_curses_color" != xyes], [ax_cv_curses_color=no]) AS_IF([test "x$ax_cv_curses_obsolete" != xyes], [ax_cv_curses_obsolete=no]) LIBS=$ax_saved_LIBS ])dnl mosh-1.2.5/m4/pkg.m4000066400000000000000000000130231255051512600140310ustar00rootroot00000000000000# pkg.m4 - Macros to locate and utilise pkg-config. -*- Autoconf -*- # serial 1 (pkg-config-0.24) # # Copyright © 2004 Scott James Remnant . # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation; either version 2 of the License, or # (at your option) any later version. # # This program is distributed in the hope that it will be useful, but # WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU # General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, write to the Free Software # Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. # # As a special exception to the GNU General Public License, if you # distribute this file as part of a program that contains a # configuration script generated by Autoconf, you may include it under # the same distribution terms that you use for the rest of that program. # PKG_PROG_PKG_CONFIG([MIN-VERSION]) # ---------------------------------- AC_DEFUN([PKG_PROG_PKG_CONFIG], [m4_pattern_forbid([^_?PKG_[A-Z_]+$]) m4_pattern_allow([^PKG_CONFIG(_(PATH|LIBDIR|SYSROOT_DIR|ALLOW_SYSTEM_(CFLAGS|LIBS)))?$]) m4_pattern_allow([^PKG_CONFIG_(DISABLE_UNINSTALLED|TOP_BUILD_DIR|DEBUG_SPEW)$]) AC_ARG_VAR([PKG_CONFIG], [path to pkg-config utility]) AC_ARG_VAR([PKG_CONFIG_PATH], [directories to add to pkg-config's search path]) AC_ARG_VAR([PKG_CONFIG_LIBDIR], [path overriding pkg-config's built-in search path]) if test "x$ac_cv_env_PKG_CONFIG_set" != "xset"; then AC_PATH_TOOL([PKG_CONFIG], [pkg-config]) fi if test -n "$PKG_CONFIG"; then _pkg_min_version=m4_default([$1], [0.9.0]) AC_MSG_CHECKING([pkg-config is at least version $_pkg_min_version]) if $PKG_CONFIG --atleast-pkgconfig-version $_pkg_min_version; then AC_MSG_RESULT([yes]) else AC_MSG_RESULT([no]) PKG_CONFIG="" fi fi[]dnl ])# PKG_PROG_PKG_CONFIG # PKG_CHECK_EXISTS(MODULES, [ACTION-IF-FOUND], [ACTION-IF-NOT-FOUND]) # # Check to see whether a particular set of modules exists. Similar # to PKG_CHECK_MODULES(), but does not set variables or print errors. # # Please remember that m4 expands AC_REQUIRE([PKG_PROG_PKG_CONFIG]) # only at the first occurence in configure.ac, so if the first place # it's called might be skipped (such as if it is within an "if", you # have to call PKG_CHECK_EXISTS manually # -------------------------------------------------------------- AC_DEFUN([PKG_CHECK_EXISTS], [AC_REQUIRE([PKG_PROG_PKG_CONFIG])dnl if test -n "$PKG_CONFIG" && \ AC_RUN_LOG([$PKG_CONFIG --exists --print-errors "$1"]); then m4_default([$2], [:]) m4_ifvaln([$3], [else $3])dnl fi]) # _PKG_CONFIG([VARIABLE], [COMMAND], [MODULES]) # --------------------------------------------- m4_define([_PKG_CONFIG], [if test -n "$$1"; then pkg_cv_[]$1="$$1" elif test -n "$PKG_CONFIG"; then PKG_CHECK_EXISTS([$3], [pkg_cv_[]$1=`$PKG_CONFIG --[]$2 "$3" 2>/dev/null` test "x$?" != "x0" && pkg_failed=yes ], [pkg_failed=yes]) else pkg_failed=untried fi[]dnl ])# _PKG_CONFIG # _PKG_SHORT_ERRORS_SUPPORTED # ----------------------------- AC_DEFUN([_PKG_SHORT_ERRORS_SUPPORTED], [AC_REQUIRE([PKG_PROG_PKG_CONFIG]) if $PKG_CONFIG --atleast-pkgconfig-version 0.20; then _pkg_short_errors_supported=yes else _pkg_short_errors_supported=no fi[]dnl ])# _PKG_SHORT_ERRORS_SUPPORTED # PKG_CHECK_MODULES(VARIABLE-PREFIX, MODULES, [ACTION-IF-FOUND], # [ACTION-IF-NOT-FOUND]) # # # Note that if there is a possibility the first call to # PKG_CHECK_MODULES might not happen, you should be sure to include an # explicit call to PKG_PROG_PKG_CONFIG in your configure.ac # # # -------------------------------------------------------------- AC_DEFUN([PKG_CHECK_MODULES], [AC_REQUIRE([PKG_PROG_PKG_CONFIG])dnl AC_ARG_VAR([$1][_CFLAGS], [C compiler flags for $1, overriding pkg-config])dnl AC_ARG_VAR([$1][_LIBS], [linker flags for $1, overriding pkg-config])dnl pkg_failed=no AC_MSG_CHECKING([for $1]) _PKG_CONFIG([$1][_CFLAGS], [cflags], [$2]) _PKG_CONFIG([$1][_LIBS], [libs], [$2]) m4_define([_PKG_TEXT], [Alternatively, you may set the environment variables $1[]_CFLAGS and $1[]_LIBS to avoid the need to call pkg-config. See the pkg-config man page for more details.]) if test $pkg_failed = yes; then AC_MSG_RESULT([no]) _PKG_SHORT_ERRORS_SUPPORTED if test $_pkg_short_errors_supported = yes; then $1[]_PKG_ERRORS=`$PKG_CONFIG --short-errors --print-errors --cflags --libs "$2" 2>&1` else $1[]_PKG_ERRORS=`$PKG_CONFIG --print-errors --cflags --libs "$2" 2>&1` fi # Put the nasty error message in config.log where it belongs echo "$$1[]_PKG_ERRORS" >&AS_MESSAGE_LOG_FD m4_default([$4], [AC_MSG_ERROR( [Package requirements ($2) were not met: $$1_PKG_ERRORS Consider adjusting the PKG_CONFIG_PATH environment variable if you installed software in a non-standard prefix. _PKG_TEXT])[]dnl ]) elif test $pkg_failed = untried; then AC_MSG_RESULT([no]) m4_default([$4], [AC_MSG_FAILURE( [The pkg-config script could not be found or is too old. Make sure it is in your PATH or set the PKG_CONFIG environment variable to the full path to pkg-config. _PKG_TEXT To get pkg-config, see .])[]dnl ]) else $1[]_CFLAGS=$pkg_cv_[]$1[]_CFLAGS $1[]_LIBS=$pkg_cv_[]$1[]_LIBS AC_MSG_RESULT([yes]) $3 fi[]dnl ])# PKG_CHECK_MODULES mosh-1.2.5/macosx/000077500000000000000000000000001255051512600137615ustar00rootroot00000000000000mosh-1.2.5/macosx/.gitignore000066400000000000000000000000471255051512600157520ustar00rootroot00000000000000/mosh-package.pmdoc /Mosh*.pkg /prefix mosh-1.2.5/macosx/Distribution.in000066400000000000000000000013471255051512600167750ustar00rootroot00000000000000 @PACKAGE_VERSION@ #edu.mit.mosh.mosh.pkg mosh-1.2.5/macosx/build.sh000077500000000000000000000111521255051512600154170ustar00rootroot00000000000000#!/bin/bash # # This script is known to work on: # OS X 10.5.8, Xcode 3.1.2, SDK 10.5, MacPorts 2.3.3 # OS X 10.9.5, Xcode 5.1.1, SDK 10.9, MacPorts 2.3.2 # OS X 10.10.3, XCode 6.3.2, SDK 10.10, Homebrew 0.9.5/8da6986 # # You may need to set PATH to include the location of your # PackageMaker binary, if your system is old enough to need that. # Setting MACOSX_DEPLOYMENT_TARGET will select an SDK as usual. # # If you are using Homebrew, you should install protobuf (and any # other future Homebrew dependencies) with # `--universal --build-bottle`. # The first option should be fairly obvious; the second has the side # effect of disabling Homebrew's overzealous processor optimization # with (effectively) `-march=native`. # set -e protobuf_LIBS=$(l=libprotobuf.a; for i in /opt/local/lib /usr/local/lib; do if [ -f $i/$l ]; then echo $i/$l; fi; done) if [ -z "$protobuf_LIBS" ]; then echo "Can't find libprotobuf.a"; exit 1; fi export protobuf_LIBS if ! pkg-config --cflags protobuf > /dev/null 2>&1; then protobuf_CFLAGS=-I$(for i in /opt /usr; do d=$i/local/include; if [ -d $d/google/protobuf ]; then echo $d; fi; done) if [ "$protobuf_CFLAGS" = "-I" ]; then echo "Can't find protobuf includes"; exit 1; fi export protobuf_CFLAGS fi echo "Building into prefix..." # # XXX This script abuses Configure's --prefix argument badly. It uses # it as a $DESTDIR, but --prefix can also affect paths in generated # objects. That is not *currently* a problem in mosh. # PREFIX="$(pwd)/prefix" ARCHS=" ppc ppc64 i386 x86_64" pushd .. > /dev/null if [ ! -f configure ]; then echo "Running autogen." PATH=/opt/local/bin:$PATH ./autogen.sh fi # # Build archs one by one. # for arch in $ARCHS; do echo "Building for ${arch}..." prefix="${PREFIX}_${arch}" rm -rf "${prefix}" mkdir "${prefix}" if ./configure --prefix="${prefix}/local" \ CC="cc -arch ${arch}" CPP="cc -arch ${arch} -E" CXX="c++ -arch ${arch}" \ TINFO_LIBS=-lncurses \ OPENSSL_CFLAGS=" " OPENSSL_LIBS="-lssl -lcrypto -lz" && make clean && make install -j8 && rm -f "${prefix}/etc" then # mosh-client built with Xcode 3.1.2 bus-errors if the binary is stripped. # strip "${prefix}/local/bin/mosh-client" "${prefix}/local/bin/mosh-server" BUILT_ARCHS="$BUILT_ARCHS $arch" fi done if [ -z "$BUILT_ARCHS" ]; then echo "No architectures built successfully" exit 1 fi echo "Building universal binaries for archs ${BUILT_ARCHS}..." rm -rf "$PREFIX" # Copy one architecture to get all files into place. for arch in $BUILT_ARCHS; do cp -Rp "${PREFIX}_${arch}" "${PREFIX}" break done # Build fat binaries # XXX will break with spaces in pathname for prog in local/bin/mosh-client local/bin/mosh-server; do archprogs= for arch in $BUILT_ARCHS; do archprogs="$archprogs ${PREFIX}_${arch}/$prog" done lipo -create $archprogs -output "${PREFIX}/$prog" done perl -wlpi -e 's{#!/usr/bin/env perl}{#!/usr/bin/perl}' "$PREFIX/local/bin/mosh" popd > /dev/null PACKAGE_VERSION=$(cat ../VERSION) OUTFILE="$PACKAGE_VERSION.pkg" rm -f "$OUTFILE" if which -s pkgbuild; then # To replace PackageMaker, you: # * make a bare package with the build products # * essentially take the Distribution file that PackageMaker generated and # use it as the --distribution input file for productbuild echo "Preprocessing package description..." PKGID=edu.mit.mosh.mosh.pkg for file in Distribution; do sed -e "s/@PACKAGE_VERSION@/${PACKAGE_VERSION}/g" ${file}.in > ${file} done echo "Running pkgbuild/productbuild..." mkdir -p Resources/en.lproj cp -p copying.rtf Resources/en.lproj/License cp -p readme.rtf Resources/en.lproj/Readme pkgbuild --root "$PREFIX" --identifier $PKGID $PKGID productbuild --distribution Distribution \ --resources Resources \ --package-path . \ "$OUTFILE" echo "Cleaning up..." rm -rf $PKGID else echo "Preprocessing package description..." INDIR=mosh-package.pmdoc.in OUTDIR=mosh-package.pmdoc mkdir -p "$OUTDIR" pushd "$INDIR" > /dev/null for file in * do sed -e 's/$PACKAGE_VERSION/'"$PACKAGE_VERSION"'/g' "$file" > "../$OUTDIR/$file" done popd > /dev/null echo "Running PackageMaker..." env PATH="/Applications/PackageMaker.app/Contents/MacOS:/Developer/Applications/Utilities/PackageMaker.app/Contents/MacOS:$PATH" PackageMaker -d mosh-package.pmdoc -o "$OUTFILE" -i edu.mit.mosh.mosh.pkg echo "Cleaning up..." rm -rf "$OUTDIR" fi if [ -f "$OUTFILE" ]; then echo "Successfully built $OUTFILE with archs ${BUILT_ARCHS}." else echo "There was an error building $OUTFILE." false fi mosh-1.2.5/macosx/copying.rtf000066400000000000000000001050731255051512600161540ustar00rootroot00000000000000{\rtf1\ansi\ansicpg1252\cocoartf1138\cocoasubrtf320 {\fonttbl\f0\fnil\fcharset0 LucidaGrande;} {\colortbl;\red255\green255\blue255;} \pard\tx560\tx1120\tx1680\tx2240\tx2800\tx3360\tx3920\tx4480\tx5040\tx5600\tx6160\tx6720\pardirnatural \f0\fs26 \cf0 GNU GENERAL PUBLIC LICENSE\ Version 3, 29 June 2007\ \ Copyright (C) 2007 Free Software Foundation, Inc. \ Everyone is permitted to copy and distribute verbatim copies\ of this license document, but changing it is not allowed.\ \ Preamble\ \ The GNU General Public License is a free, copyleft license for software and other kinds of works.\ \ The licenses for most software and other practical works are designed to take away your freedom to share and change the works. By contrast, the GNU General Public License is intended to guarantee your freedom to share and change all versions of a program--to make sure it remains free software for all its users. We, the Free Software Foundation, use the GNU General Public License for most of our software; it applies also to any other work released this way by its authors. You can apply it to your programs, too.\ \ When we speak of free software, we are referring to freedom, not price. Our General Public Licenses are designed to make sure that you have the freedom to distribute copies of free software (and charge for them if you wish), that you receive source code or can get it if you want it, that you can change the software or use pieces of it in new free programs, and that you know you can do these things.\ \ To protect your rights, we need to prevent others from denying you these rights or asking you to surrender the rights. Therefore, you have certain responsibilities if you distribute copies of the software, or if you modify it: responsibilities to respect the freedom of others.\ \ For example, if you distribute copies of such a program, whether gratis or for a fee, you must pass on to the recipients the same freedoms that you received. You must make sure that they, too, receive or can get the source code. And you must show them these terms so they know their rights.\ \ Developers that use the GNU GPL protect your rights with two steps: (1) assert copyright on the software, and (2) offer you this License giving you legal permission to copy, distribute and/or modify it.\ \ For the developers' and authors' protection, the GPL clearly explains that there is no warranty for this free software. For both users' and authors' sake, the GPL requires that modified versions be marked as changed, so that their problems will not be attributed erroneously to authors of previous versions.\ \ Some devices are designed to deny users access to install or run modified versions of the software inside them, although the manufacturer can do so. This is fundamentally incompatible with the aim of protecting users' freedom to change the software. The systematic pattern of such abuse occurs in the area of products for individuals to use, which is precisely where it is most unacceptable. Therefore, we have designed this version of the GPL to prohibit the practice for those products. If such problems arise substantially in other domains, we stand ready to extend this provision to those domains in future versions of the GPL, as needed to protect the freedom of users.\ \ Finally, every program is threatened constantly by software patents. States should not allow patents to restrict development and use of software on general-purpose computers, but in those that do, we wish to avoid the special danger that patents applied to a free program could make it effectively proprietary. To prevent this, the GPL assures that patents cannot be used to render the program non-free.\ \ The precise terms and conditions for copying, distribution and modification follow.\ \ TERMS AND CONDITIONS\ \ 0. Definitions.\ \ "This License" refers to version 3 of the GNU General Public License.\ \ "Copyright" also means copyright-like laws that apply to other kinds of works, such as semiconductor masks.\ \ "The Program" refers to any copyrightable work licensed under this License. Each licensee is addressed as "you". "Licensees" and "recipients" may be individuals or organizations.\ \ To "modify" a work means to copy from or adapt all or part of the work in a fashion requiring copyright permission, other than the making of an exact copy. The resulting work is called a "modified version" of the earlier work or a work "based on" the earlier work.\ \ A "covered work" means either the unmodified Program or a work based on the Program.\ \ To "propagate" a work means to do anything with it that, without permission, would make you directly or secondarily liable for infringement under applicable copyright law, except executing it on a computer or modifying a private copy. Propagation includes copying, distribution (with or without modification), making available to the public, and in some countries other activities as well.\ \ To "convey" a work means any kind of propagation that enables other parties to make or receive copies. Mere interaction with a user through a computer network, with no transfer of a copy, is not conveying.\ \ An interactive user interface displays "Appropriate Legal Notices" to the extent that it includes a convenient and prominently visible feature that (1) displays an appropriate copyright notice, and (2) tells the user that there is no warranty for the work (except to the extent that warranties are provided), that licensees may convey the work under this License, and how to view a copy of this License. If the interface presents a list of user commands or options, such as a menu, a prominent item in the list meets this criterion.\ \ 1. Source Code.\ \ The "source code" for a work means the preferred form of the work for making modifications to it. "Object code" means any non-source form of a work.\ \ A "Standard Interface" means an interface that either is an official standard defined by a recognized standards body, or, in the case of interfaces specified for a particular programming language, one that is widely used among developers working in that language.\ \ The "System Libraries" of an executable work include anything, other than the work as a whole, that (a) is included in the normal form of packaging a Major Component, but which is not part of that Major Component, and (b) serves only to enable use of the work with that Major Component, or to implement a Standard Interface for which an implementation is available to the public in source code form. A "Major Component", in this context, means a major essential component (kernel, window system, and so on) of the specific operating system (if any) on which the executable work runs, or a compiler used to produce the work, or an object code interpreter used to run it.\ \ The "Corresponding Source" for a work in object code form means all the source code needed to generate, install, and (for an executable work) run the object code and to modify the work, including scripts to control those activities. However, it does not include the work's System Libraries, or general-purpose tools or generally available free programs which are used unmodified in performing those activities but which are not part of the work. For example, Corresponding Source includes interface definition files associated with source files for the work, and the source code for shared libraries and dynamically linked subprograms that the work is specifically designed to require, such as by intimate data communication or control flow between those subprograms and other parts of the work.\ \ The Corresponding Source need not include anything that users can regenerate automatically from other parts of the Corresponding Source.\ \ The Corresponding Source for a work in source code form is that same work.\ \ 2. Basic Permissions.\ \ All rights granted under this License are granted for the term of copyright on the Program, and are irrevocable provided the stated conditions are met. This License explicitly affirms your unlimited permission to run the unmodified Program. The output from running a covered work is covered by this License only if the output, given its content, constitutes a covered work. This License acknowledges your rights of fair use or other equivalent, as provided by copyright law.\ \ You may make, run and propagate covered works that you do not convey, without conditions so long as your license otherwise remains in force. You may convey covered works to others for the sole purpose of having them make modifications exclusively for you, or provide you with facilities for running those works, provided that you comply with the terms of this License in conveying all material for which you do not control copyright. Those thus making or running the covered works for you must do so exclusively on your behalf, under your direction and control, on terms that prohibit them from making any copies of your copyrighted material outside their relationship with you.\ \ Conveying under any other circumstances is permitted solely under the conditions stated below. Sublicensing is not allowed; section 10 makes it unnecessary.\ \ 3. Protecting Users' Legal Rights From Anti-Circumvention Law.\ \ No covered work shall be deemed part of an effective technological measure under any applicable law fulfilling obligations under article 11 of the WIPO copyright treaty adopted on 20 December 1996, or similar laws prohibiting or restricting circumvention of such measures.\ \ When you convey a covered work, you waive any legal power to forbid circumvention of technological measures to the extent such circumvention is effected by exercising rights under this License with respect to the covered work, and you disclaim any intention to limit operation or modification of the work as a means of enforcing, against the work's users, your or third parties' legal rights to forbid circumvention of technological measures.\ \ 4. Conveying Verbatim Copies.\ \ You may convey verbatim copies of the Program's source code as you receive it, in any medium, provided that you conspicuously and appropriately publish on each copy an appropriate copyright notice; keep intact all notices stating that this License and any non-permissive terms added in accord with section 7 apply to the code; keep intact all notices of the absence of any warranty; and give all recipients a copy of this License along with the Program.\ \ You may charge any price or no price for each copy that you convey, and you may offer support or warranty protection for a fee.\ \ 5. Conveying Modified Source Versions.\ \ You may convey a work based on the Program, or the modifications to produce it from the Program, in the form of source code under the terms of section 4, provided that you also meet all of these conditions:\ \ a) The work must carry prominent notices stating that you modified it, and giving a relevant date.\ \ b) The work must carry prominent notices stating that it is released under this License and any conditions added under section 7. This requirement modifies the requirement in section 4 to "keep intact all notices".\ \ c) You must license the entire work, as a whole, under this License to anyone who comes into possession of a copy. This License will therefore apply, along with any applicable section 7 additional terms, to the whole of the work, and all its parts, regardless of how they are packaged. This License gives no permission to license the work in any other way, but it does not invalidate such permission if you have separately received it.\ \ d) If the work has interactive user interfaces, each must display Appropriate Legal Notices; however, if the Program has interactive interfaces that do not display Appropriate Legal Notices, your work need not make them do so.\ \ A compilation of a covered work with other separate and independent works, which are not by their nature extensions of the covered work, and which are not combined with it such as to form a larger program, in or on a volume of a storage or distribution medium, is called an "aggregate" if the compilation and its resulting copyright are not used to limit the access or legal rights of the compilation's users beyond what the individual works permit. Inclusion of a covered work in an aggregate does not cause this License to apply to the other parts of the aggregate.\ \ 6. Conveying Non-Source Forms.\ \ You may convey a covered work in object code form under the terms of sections 4 and 5, provided that you also convey the machine-readable Corresponding Source under the terms of this License, in one of these ways:\ \ a) Convey the object code in, or embodied in, a physical product (including a physical distribution medium), accompanied by the Corresponding Source fixed on a durable physical medium customarily used for software interchange.\ \ b) Convey the object code in, or embodied in, a physical product (including a physical distribution medium), accompanied by a written offer, valid for at least three years and valid for as long as you offer spare parts or customer support for that product model, to give anyone who possesses the object code either (1) a copy of the Corresponding Source for all the software in the product that is covered by this License, on a durable physical medium customarily used for software interchange, for a price no more than your reasonable cost of physically performing this conveying of source, or (2) access to copy the Corresponding Source from a network server at no charge.\ \ c) Convey individual copies of the object code with a copy of the written offer to provide the Corresponding Source. This alternative is allowed only occasionally and noncommercially, and only if you received the object code with such an offer, in accord with subsection 6b.\ \ d) Convey the object code by offering access from a designated place (gratis or for a charge), and offer equivalent access to the Corresponding Source in the same way through the same place at no further charge. You need not require recipients to copy the Corresponding Source along with the object code. If the place to copy the object code is a network server, the Corresponding Source may be on a different server (operated by you or a third party) that supports equivalent copying facilities, provided you maintain clear directions next to the object code saying where to find the Corresponding Source. Regardless of what server hosts the Corresponding Source, you remain obligated to ensure that it is available for as long as needed to satisfy these requirements.\ \ e) Convey the object code using peer-to-peer transmission, provided you inform other peers where the object code and Corresponding Source of the work are being offered to the general public at no charge under subsection 6d.\ \ A separable portion of the object code, whose source code is excluded from the Corresponding Source as a System Library, need not be included in conveying the object code work.\ \ A "User Product" is either (1) a "consumer product", which means any tangible personal property which is normally used for personal, family, or household purposes, or (2) anything designed or sold for incorporation into a dwelling. In determining whether a product is a consumer product, doubtful cases shall be resolved in favor of coverage. For a particular product received by a particular user, "normally used" refers to a typical or common use of that class of product, regardless of the status of the particular user or of the way in which the particular user actually uses, or expects or is expected to use, the product. A product is a consumer product regardless of whether the product has substantial commercial, industrial or non-consumer uses, unless such uses represent the only significant mode of use of the product.\ \ "Installation Information" for a User Product means any methods, procedures, authorization keys, or other information required to install and execute modified versions of a covered work in that User Product from a modified version of its Corresponding Source. The information must suffice to ensure that the continued functioning of the modified object code is in no case prevented or interfered with solely because modification has been made.\ \ If you convey an object code work under this section in, or with, or specifically for use in, a User Product, and the conveying occurs as part of a transaction in which the right of possession and use of the User Product is transferred to the recipient in perpetuity or for a fixed term (regardless of how the transaction is characterized), the Corresponding Source conveyed under this section must be accompanied by the Installation Information. But this requirement does not apply if neither you nor any third party retains the ability to install modified object code on the User Product (for example, the work has been installed in ROM).\ \ The requirement to provide Installation Information does not include a requirement to continue to provide support service, warranty, or updates for a work that has been modified or installed by the recipient, or for the User Product in which it has been modified or installed. Access to a network may be denied when the modification itself materially and adversely affects the operation of the network or violates the rules and protocols for communication across the network.\ \ Corresponding Source conveyed, and Installation Information provided, in accord with this section must be in a format that is publicly documented (and with an implementation available to the public in source code form), and must require no special password or key for unpacking, reading or copying.\ \ 7. Additional Terms.\ \ "Additional permissions" are terms that supplement the terms of this License by making exceptions from one or more of its conditions. Additional permissions that are applicable to the entire Program shall be treated as though they were included in this License, to the extent that they are valid under applicable law. If additional permissions apply only to part of the Program, that part may be used separately under those permissions, but the entire Program remains governed by this License without regard to the additional permissions.\ \ When you convey a copy of a covered work, you may at your option remove any additional permissions from that copy, or from any part of it. (Additional permissions may be written to require their own removal in certain cases when you modify the work.) You may place additional permissions on material, added by you to a covered work, for which you have or can give appropriate copyright permission.\ \ Notwithstanding any other provision of this License, for material you add to a covered work, you may (if authorized by the copyright holders of that material) supplement the terms of this License with terms:\ \ a) Disclaiming warranty or limiting liability differently from the terms of sections 15 and 16 of this License; or\ \ b) Requiring preservation of specified reasonable legal notices or author attributions in that material or in the Appropriate Legal Notices displayed by works containing it; or\ \ c) Prohibiting misrepresentation of the origin of that material, or requiring that modified versions of such material be marked in reasonable ways as different from the original version; or\ \ d) Limiting the use for publicity purposes of names of licensors or authors of the material; or\ \ e) Declining to grant rights under trademark law for use of some trade names, trademarks, or service marks; or\ \ f) Requiring indemnification of licensors and authors of that material by anyone who conveys the material (or modified versions of it) with contractual assumptions of liability to the recipient, for any liability that these contractual assumptions directly impose on those licensors and authors.\ \ All other non-permissive additional terms are considered "further restrictions" within the meaning of section 10. If the Program as you received it, or any part of it, contains a notice stating that it is governed by this License along with a term that is a further restriction, you may remove that term. If a license document contains a further restriction but permits relicensing or conveying under this License, you may add to a covered work material governed by the terms of that license document, provided that the further restriction does not survive such relicensing or conveying.\ \ If you add terms to a covered work in accord with this section, you must place, in the relevant source files, a statement of the additional terms that apply to those files, or a notice indicating where to find the applicable terms.\ \ Additional terms, permissive or non-permissive, may be stated in the form of a separately written license, or stated as exceptions; the above requirements apply either way.\ \ 8. Termination.\ \ You may not propagate or modify a covered work except as expressly provided under this License. Any attempt otherwise to propagate or modify it is void, and will automatically terminate your rights under this License (including any patent licenses granted under the third paragraph of section 11).\ \ However, if you cease all violation of this License, then your license from a particular copyright holder is reinstated (a) provisionally, unless and until the copyright holder explicitly and finally terminates your license, and (b) permanently, if the copyright holder fails to notify you of the violation by some reasonable means prior to 60 days after the cessation.\ \ Moreover, your license from a particular copyright holder is reinstated permanently if the copyright holder notifies you of the violation by some reasonable means, this is the first time you have received notice of violation of this License (for any work) from that copyright holder, and you cure the violation prior to 30 days after your receipt of the notice.\ \ Termination of your rights under this section does not terminate the licenses of parties who have received copies or rights from you under this License. If your rights have been terminated and not permanently reinstated, you do not qualify to receive new licenses for the same material under section 10.\ \ 9. Acceptance Not Required for Having Copies.\ \ You are not required to accept this License in order to receive or run a copy of the Program. Ancillary propagation of a covered work occurring solely as a consequence of using peer-to-peer transmission to receive a copy likewise does not require acceptance. However, nothing other than this License grants you permission to propagate or modify any covered work. These actions infringe copyright if you do not accept this License. Therefore, by modifying or propagating a covered work, you indicate your acceptance of this License to do so.\ \ 10. Automatic Licensing of Downstream Recipients.\ \ Each time you convey a covered work, the recipient automatically receives a license from the original licensors, to run, modify and propagate that work, subject to this License. You are not responsible for enforcing compliance by third parties with this License.\ \ An "entity transaction" is a transaction transferring control of an organization, or substantially all assets of one, or subdividing an organization, or merging organizations. If propagation of a covered work results from an entity transaction, each party to that transaction who receives a copy of the work also receives whatever licenses to the work the party's predecessor in interest had or could give under the previous paragraph, plus a right to possession of the Corresponding Source of the work from the predecessor in interest, if the predecessor has it or can get it with reasonable efforts.\ \ You may not impose any further restrictions on the exercise of the rights granted or affirmed under this License. For example, you may not impose a license fee, royalty, or other charge for exercise of rights granted under this License, and you may not initiate litigation (including a cross-claim or counterclaim in a lawsuit) alleging that any patent claim is infringed by making, using, selling, offering for sale, or importing the Program or any portion of it.\ \ 11. Patents.\ \ A "contributor" is a copyright holder who authorizes use under this License of the Program or a work on which the Program is based. The work thus licensed is called the contributor's "contributor version".\ \ A contributor's "essential patent claims" are all patent claims owned or controlled by the contributor, whether already acquired or hereafter acquired, that would be infringed by some manner, permitted by this License, of making, using, or selling its contributor version, but do not include claims that would be infringed only as a consequence of further modification of the contributor version. For purposes of this definition, "control" includes the right to grant patent sublicenses in a manner consistent with the requirements of this License.\ \ Each contributor grants you a non-exclusive, worldwide, royalty-free patent license under the contributor's essential patent claims, to make, use, sell, offer for sale, import and otherwise run, modify and propagate the contents of its contributor version.\ \ In the following three paragraphs, a "patent license" is any express agreement or commitment, however denominated, not to enforce a patent (such as an express permission to practice a patent or covenant not to sue for patent infringement). To "grant" such a patent license to a party means to make such an agreement or commitment not to enforce a patent against the party.\ \ If you convey a covered work, knowingly relying on a patent license, and the Corresponding Source of the work is not available for anyone to copy, free of charge and under the terms of this License, through a publicly available network server or other readily accessible means, then you must either (1) cause the Corresponding Source to be so available, or (2) arrange to deprive yourself of the benefit of the patent license for this particular work, or (3) arrange, in a manner consistent with the requirements of this License, to extend the patent license to downstream recipients. "Knowingly relying" means you have actual knowledge that, but for the patent license, your conveying the covered work in a country, or your recipient's use of the covered work in a country, would infringe one or more identifiable patents in that country that you have reason to believe are valid.\ \ If, pursuant to or in connection with a single transaction or arrangement, you convey, or propagate by procuring conveyance of, a covered work, and grant a patent license to some of the parties receiving the covered work authorizing them to use, propagate, modify or convey a specific copy of the covered work, then the patent license you grant is automatically extended to all recipients of the covered work and works based on it.\ \ A patent license is "discriminatory" if it does not include within the scope of its coverage, prohibits the exercise of, or is conditioned on the non-exercise of one or more of the rights that are specifically granted under this License. You may not convey a covered work if you are a party to an arrangement with a third party that is in the business of distributing software, under which you make payment to the third party based on the extent of your activity of conveying the work, and under which the third party grants, to any of the parties who would receive the covered work from you, a discriminatory patent license (a) in connection with copies of the covered work conveyed by you (or copies made from those copies), or (b) primarily for and in connection with specific products or compilations that contain the covered work, unless you entered into that arrangement, or that patent license was granted, prior to 28 March 2007.\ \ Nothing in this License shall be construed as excluding or limiting any implied license or other defenses to infringement that may otherwise be available to you under applicable patent law.\ \ 12. No Surrender of Others' Freedom.\ \ If conditions are imposed on you (whether by court order, agreement or otherwise) that contradict the conditions of this License, they do not excuse you from the conditions of this License. If you cannot convey a covered work so as to satisfy simultaneously your obligations under this License and any other pertinent obligations, then as a consequence you may not convey it at all. For example, if you agree to terms that obligate you to collect a royalty for further conveying from those to whom you convey the Program, the only way you could satisfy both those terms and this License would be to refrain entirely from conveying the Program.\ \ 13. Use with the GNU Affero General Public License.\ \ Notwithstanding any other provision of this License, you have permission to link or combine any covered work with a work licensed under version 3 of the GNU Affero General Public License into a single combined work, and to convey the resulting work. The terms of this License will continue to apply to the part which is the covered work, but the special requirements of the GNU Affero General Public License, section 13, concerning interaction through a network will apply to the combination as such.\ \ 14. Revised Versions of this License.\ \ The Free Software Foundation may publish revised and/or new versions of the GNU General Public License from time to time. Such new versions will be similar in spirit to the present version, but may differ in detail to address new problems or concerns.\ \ Each version is given a distinguishing version number. If the Program specifies that a certain numbered version of the GNU General Public License "or any later version" applies to it, you have the option of following the terms and conditions either of that numbered version or of any later version published by the Free Software Foundation. If the Program does not specify a version number of the GNU General Public License, you may choose any version ever published by the Free Software Foundation.\ \ If the Program specifies that a proxy can decide which future versions of the GNU General Public License can be used, that proxy's public statement of acceptance of a version permanently authorizes you to choose that version for the Program.\ \ Later license versions may give you additional or different permissions. However, no additional obligations are imposed on any author or copyright holder as a result of your choosing to follow a later version.\ \ 15. Disclaimer of Warranty.\ \ THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION.\ \ 16. Limitation of Liability.\ \ IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES.\ \ 17. Interpretation of Sections 15 and 16.\ \ If the disclaimer of warranty and limitation of liability provided above cannot be given local legal effect according to their terms, reviewing courts shall apply local law that most closely approximates an absolute waiver of all civil liability in connection with the Program, unless a warranty or assumption of liability accompanies a copy of the Program in return for a fee.\ \ END OF TERMS AND CONDITIONS\ \ How to Apply These Terms to Your New Programs\ \ If you develop a new program, and you want it to be of the greatest possible use to the public, the best way to achieve this is to make it free software which everyone can redistribute and change under these terms.\ \ To do so, attach the following notices to the program. It is safest to attach them to the start of each source file to most effectively state the exclusion of warranty; and each file should have at least the "copyright" line and a pointer to where the full notice is found.\ \ \ Copyright (C) \ \ This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.\ \ This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.\ \ You should have received a copy of the GNU General Public License along with this program. If not, see .\ \ Also add information on how to contact you by electronic and paper mail.\ \ If the program does terminal interaction, make it output a short notice like this when it starts in an interactive mode:\ \ Copyright (C) This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'. This is free software, and you are welcome to redistribute it under certain conditions; type `show c' for details.\ \ The hypothetical commands `show w' and `show c' should show the appropriate parts of the General Public License. Of course, your program's commands might be different; for a GUI interface, you would use an "about box".\ \ You should also get your employer (if you work as a programmer) or school, if any, to sign a "copyright disclaimer" for the program, if necessary. For more information on this, and how to apply and follow the GNU GPL, see .\ \ The GNU General Public License does not permit incorporating your program into proprietary programs. If your program is a subroutine library, you may consider it more useful to permit linking proprietary applications with the library. If this is what you want to do, use the GNU Lesser General Public License instead of this License. But first, please read .} mosh-1.2.5/macosx/mosh-package.pmdoc.in/000077500000000000000000000000001255051512600200265ustar00rootroot00000000000000mosh-1.2.5/macosx/mosh-package.pmdoc.in/01prefix-contents.xml000066400000000000000000000027521255051512600240470ustar00rootroot00000000000000 group owner group owner group owner group owner group owner group owner group owner group owner group owner group owner group owner group owner mosh-1.2.5/macosx/mosh-package.pmdoc.in/01prefix.xml000066400000000000000000000017531255051512600222140ustar00rootroot00000000000000 edu.mit.mosh.mosh.pkg $PACKAGE_VERSION prefix /usr installTo relocatable installFrom.path identifier parent includeRoot installTo.path installFrom.isRelativeType version 01prefix-contents.xml /\.gitignore$ /\.DS_Store$ mosh-1.2.5/macosx/mosh-package.pmdoc.in/index.xml000066400000000000000000000023761255051512600216670ustar00rootroot00000000000000 Mosh $PACKAGE_VERSION Mosh.pkg edu.mit.mosh Mosh is a remote terminal application that supports intermittent connectivity, allows roaming, and provides speculative local echo and line editing of user keystrokes. / copying.rtf readme.rtf 01prefix.xml properties.title properties.userDomain description mosh-1.2.5/macosx/readme.rtf000066400000000000000000000154251255051512600157420ustar00rootroot00000000000000{\rtf1\ansi\ansicpg1252\cocoartf1138\cocoasubrtf320 {\fonttbl\f0\fswiss\fcharset0 Helvetica;\f1\fmodern\fcharset0 Courier;} {\colortbl;\red255\green255\blue255;\red38\green38\blue38;\red246\green246\blue246;\red51\green108\blue190; } {\*\listtable{\list\listtemplateid1\listhybrid{\listlevel\levelnfc23\levelnfcn23\leveljc0\leveljcn0\levelfollow0\levelstartat1\levelspace360\levelindent0{\*\levelmarker \{disc\}}{\leveltext\leveltemplateid1\'01\uc0\u8226 ;}{\levelnumbers;}\fi-360\li720\lin720 }{\listname ;}\listid1} {\list\listtemplateid2\listhybrid{\listlevel\levelnfc23\levelnfcn23\leveljc0\leveljcn0\levelfollow0\levelstartat1\levelspace360\levelindent0{\*\levelmarker \{disc\}}{\leveltext\leveltemplateid101\'01\uc0\u8226 ;}{\levelnumbers;}\fi-360\li720\lin720 }{\listname ;}\listid2} {\list\listtemplateid3\listhybrid{\listlevel\levelnfc23\levelnfcn23\leveljc0\leveljcn0\levelfollow0\levelstartat1\levelspace360\levelindent0{\*\levelmarker \{disc\}}{\leveltext\leveltemplateid201\'01\uc0\u8226 ;}{\levelnumbers;}\fi-360\li720\lin720 }{\listname ;}\listid3}} {\*\listoverridetable{\listoverride\listid1\listoverridecount0\ls1}{\listoverride\listid2\listoverridecount0\ls2}{\listoverride\listid3\listoverridecount0\ls3}} \deftab720 \pard\pardeftab720\sa200 \f0\b\fs56 \cf0 Mosh: the mobile shell\ \pard\pardeftab720\sa300 \b0\fs28 \cf2 Mosh is a remote terminal application that supports intermittent connectivity, allows roaming, and provides speculative local echo and line editing of user keystrokes.\ It aims to support the typical interactive uses of SSH, plus:\ \pard\tx220\tx720\pardeftab720\li720\fi-720 \ls1\ilvl0\cf2 {\listtext \'95 }Mosh keeps the session alive if the client goes to sleep and wakes up later, or temporarily loses its Internet connection.\ {\listtext \'95 }Mosh allows the client and server to "roam" and change IP addresses, while keeping the connection alive. Unlike SSH, Mosh can be used while switching between Wi-Fi networks or from Wi-Fi to cellular data to wired Ethernet.\ \pard\tx220\tx720\pardeftab720\li720\fi-720\sa300 \ls1\ilvl0\cf2 {\listtext \'95 }The Mosh client runs a predictive model of the server's behavior in the background and tries to guess intelligently how each keystroke will affect the screen state. When it is confident in its predictions, it will show them to the user while waiting for confirmation from the server. Most typing and uses of the left- and right-arrow keys can be echoed immediately.\uc0\u8232 As a result, Mosh is usable on high-latency links, e.g. on a cellular data connection or spotty Wi-Fi. In distinction from previous attempts at local echo modes in other protocols, Mosh works properly with full-screen applications such as emacs, vi, alpine, and irssi, and automatically recovers from occasional prediction errors within an RTT. On high-latency links, Mosh underlines its predictions while they are outstanding and removes the underline when they are confirmed by the server.\ \pard\pardeftab720\sa300 \cf2 Mosh does not support X forwarding or the non-interactive uses of SSH, including port forwarding.\ \pard\pardeftab720\sa200 \b\fs48 \cf0 Other features\ \pard\tx220\tx720\pardeftab720\li720\fi-720 \ls2\ilvl0 \b0\fs28 \cf2 {\listtext \'95 }Mosh adjusts its frame rate so as not to fill up network queues on slow links, so "Control-C" always works within an RTT to halt a runaway process.\ {\listtext \'95 }Mosh warns the user when it has not heard from the server in a while.\ {\listtext \'95 }Mosh supports lossy links that lose a significant fraction of their packets.\ {\listtext \'95 }Mosh handles some Unicode edge cases better than SSH and existing terminal emulators by themselves, but requires a UTF-8 environment to run.\ {\listtext \'95 }Mosh leverages SSH to set up the connection and authenticate users. Mosh does not contain any privileged (root) code.\ \ \pard\pardeftab720\sa200 \b\fs48 \cf0 Usage\ \pard\pardeftab720\sa300 \b0\fs28 \cf2 The \f1\fs24 \cb3 mosh-client \f0\fs28 \cb1 binary must be installed on the user's machine, and the \f1\fs24 \cb3 mosh-server \f0\fs28 \cb1 binary on the remote host.\ The user runs:\ \pard\pardeftab720 \f1\fs24 \cf2 \cb3 $ mosh [user@]host\ \pard\pardeftab720\sa300 \f0\fs28 \cf2 \cb1 A command may also be specified, for example:\ \pard\pardeftab720 \f1\fs24 \cf2 \cb3 $ mosh host -- screen -r\ \pard\pardeftab720\sa300 \f0\fs28 \cf2 \cb1 If the \f1\fs24 \cb3 mosh-client \f0\fs28 \cb1 or \f1\fs24 \cb3 mosh-server \f0\fs28 \cb1 binaries are installed outside the user's PATH, \f1\fs24 \cb3 mosh \f0\fs28 \cb1 accepts the arguments \f1\fs24 \cb3 --client=PATH \f0\fs28 \cb1 and \f1\fs24 \cb3 --server=PATH \f0\fs28 \cb1 to select alternate locations. More options are documented in the mosh(1) manual page.\ Mosh supports 256-color mode as long as the user's own terminal does. Generally this means the \f1\fs24 \cb3 TERM \f0\fs28 \cb1 environment variable must be set to \f1\fs24 \cb3 xterm-256color \f0\fs28 \cb1 or \f1\fs24 \cb3 screen-256color-bce \f0\fs28 \cb1 before running \f1\fs24 \cb3 mosh \f0\fs28 \cb1 .\ \pard\pardeftab720\sa200 \b\fs48 \cf0 How it works\ \pard\pardeftab720\sa300 \b0\fs28 \cf2 The \f1\fs24 \cb3 mosh \f0\fs28 \cb1 program will SSH to \f1\fs24 \cb3 user@host \f0\fs28 \cb1 to establish the connection. SSH may prompt the user for a password or use public-key authentication to log in.\ From this point, \f1\fs24 \cb3 mosh \f0\fs28 \cb1 runs the \f1\fs24 \cb3 mosh-server \f0\fs28 \cb1 process (as the user) on the server machine. The server process listens on a high UDP port and sends its port number and an AES-128 secret key back to the client over SSH. The SSH connection is then shut down and the terminal session begins over UDP.\ If the client changes IP addresses, the server will begin sending to the client on the new IP address within a few seconds.\ To function, Mosh requires UDP datagrams to be passed between client and server. By default, \f1\fs24 \cb3 mosh \f0\fs28 \cb1 uses a port number between 60000 and 61000, but the user can select a particular port with the -p option.\ \pard\pardeftab720\sa200 \b\fs48 \cf0 More info\ \pard\tx220\tx720\pardeftab720\li720\fi-720\sa300 \ls3\ilvl0 \b0\fs28 \cf2 {\listtext \'95 }Mosh Web site:\uc0\u8232 {\field{\*\fldinst{HYPERLINK "http://mosh.mit.edu/"}}{\fldrslt \cf4 http://mosh.mit.edu}}\ \pard\tx220\tx720\pardeftab720\li720\fi-720\sa300 \ls3\ilvl0 \f1\fs24 \cf2 \cb3 {\listtext \'95 }mosh-devel@mit.edu \f0\fs28 \cb1 mailing list:\uc0\u8232 {\field{\*\fldinst{HYPERLINK "http://mailman.mit.edu/mailman/listinfo/mosh-devel"}}{\fldrslt \cf4 http://mailman.mit.edu/mailman/listinfo/mosh-devel}}\ \ls3\ilvl0 \f1\fs24 \cb3 {\listtext \'95 }mosh-users@mit.edu \f0\fs28 \cb1 mailing list:\uc0\u8232 {\field{\*\fldinst{HYPERLINK "http://mailman.mit.edu/mailman/listinfo/mosh-users"}}{\fldrslt \cf4 http://mailman.mit.edu/mailman/listinfo/mosh-users}}} mosh-1.2.5/man/000077500000000000000000000000001255051512600132425ustar00rootroot00000000000000mosh-1.2.5/man/Makefile.am000066400000000000000000000002071255051512600152750ustar00rootroot00000000000000dist_man_MANS = if BUILD_CLIENT dist_man_MANS += mosh.1 mosh-client.1 endif if BUILD_SERVER dist_man_MANS += mosh-server.1 endif mosh-1.2.5/man/mosh-client.1000066400000000000000000000045671255051512600155620ustar00rootroot00000000000000.\" Hey, EMACS: -*- nroff -*- .\" First parameter, NAME, should be all caps .\" Second parameter, SECTION, should be 1-8, maybe w/ subsection .\" other parameters are allowed: see man(7), man(1) .TH MOSH 1 "February 2012" .\" Please adjust this date whenever revising the manpage. .\" .\" Some roff macros, for reference: .\" .nh disable hyphenation .\" .hy enable hyphenation .\" .ad l left justify .\" .ad b justify to both left and right margins .\" .nf disable filling .\" .fi enable filling .\" .br insert line break .\" .sp insert n+1 empty lines .\" for manpage-specific macros, see man(7) .SH NAME mosh-client \- client-side helper for mosh .SH SYNOPSIS MOSH_KEY=KEY .B mosh-client IP PORT .br .B mosh-client \-c .br .SH DESCRIPTION \fBmosh-client\fP is a helper program for the .BR mosh (1) remote terminal application. \fBmosh\fP itself is a setup script that establishes an SSH connection, runs the server-side helper \fBmosh-server\fP, and collects the server's port number and session key. \fBmosh\fP then executes \fBmosh-client\fP with the server's IP address, port, and session key. \fBmosh-client\fP runs for the lifetime of the connection. The 22-byte base64 session key given by \fBmosh-server\fP is supplied in the MOSH_KEY environment variable. This represents a 128-bit AES key that protects the integrity and confidentiality of the session. For constructing new setup wrappers for remote execution facilities other than SSH, it may be necessary to invoke \fBmosh-client\fP directly. With the \-c option, \fBmosh-client\fP instead prints the number of colors of the terminal given by the TERM environment variable. .SH ENVIRONMENT VARIABLES .TP .B MOSH_KEY This variable must be set, and must contain a Base64-encoded cryptographic key from .BR mosh-server (1). .TP .B MOSH_ESCAPE_KEY See .BR mosh (1). .TP .B MOSH_PREDICTION_DISPLAY Controls local echo as described in .BR mosh (1). .TP .B MOSH_TITLE_NOPREFIX See .BR mosh (1). .SH SEE ALSO .BR mosh (1), .BR mosh-server (1). Project home page: .I http://mosh.mit.edu .br .SH AUTHOR mosh was written by Keith Winstein . .SH BUGS Please report bugs to \fImosh-devel@mit.edu\fP. Users may also subscribe to the .nh .I mosh-users@mit.edu .hy mailing list, at .br .nh .I http://mailman.mit.edu/mailman/listinfo/mosh-users .hy . mosh-1.2.5/man/mosh-server.1000066400000000000000000000055551255051512600156100ustar00rootroot00000000000000.\" Hey, EMACS: -*- nroff -*- .\" First parameter, NAME, should be all caps .\" Second parameter, SECTION, should be 1-8, maybe w/ subsection .\" other parameters are allowed: see man(7), man(1) .TH MOSH 1 "October 2012" .\" Please adjust this date whenever revising the manpage. .\" .\" Some roff macros, for reference: .\" .nh disable hyphenation .\" .hy enable hyphenation .\" .ad l left justify .\" .ad b justify to both left and right margins .\" .nf disable filling .\" .fi enable filling .\" .br insert line break .\" .sp insert n+1 empty lines .\" for manpage-specific macros, see man(7) .SH NAME mosh-server \- server-side helper for mosh .SH SYNOPSIS .B mosh-server new [\-s] [\-v] [\-i \fIIP\fP] [\-p \fIPORT\fP[:\fIPORT2\fP]] [\-c \fICOLORS\fP] [\-\- command...] .br .SH DESCRIPTION \fBmosh-server\fP is a helper program for the .BR mosh(1) remote terminal application. \fBmosh-server\fP binds to a high UDP port and chooses an encryption key to protect the session. It prints both on standard output, detaches from the terminal, and waits for the \fBmosh-client\fP to establish a connection. It will exit if no client has contacted it within 60 seconds. By default, \fBmosh-server\fP binds to a port between 60000 and 61000 and executes the user's login shell. On platforms with \fButempter\fP, \fBmosh-server\fP maintains an entry in the .BR utmp(5) file to indicate its process ID, whether the session is connected, and the client's current IP address. \fBmosh-server\fP exits when the client terminates the connection. .SH OPTIONS The argument "new" must be first on the command line to use command-line options. .TP .B \-s bind to the local interface used for an incoming SSH connection, given in the \fBSSH_CONNECTION\fP environment variable (for multihomed hosts) .TP .B \-v Print some debugging information even after detaching. .TP .B \-i \fIIP\fP IP address of the local interface to bind (for multihomed hosts) .TP .B \-p \fIPORT\fP[:\fIPORT2\fP] UDP port number or port-range to bind .TP .B \-c \fICOLORS\fP Number of colors to advertise to applications through TERM (e.g. 8, 256) .TP .B \-l \fINAME=VALUE\fP Locale-related environment variable to try as part of a fallback environment, if the startup environment does not specify a character set of UTF-8. .SH EXAMPLE .nf $ mosh-server MOSH CONNECT 60001 UAkFedSsVJs2LfMeKyQB5g mosh-server (mosh 1.1) [...] (copyright notice omitted) [mosh-server detached, pid = 20443] .fi .SH SEE ALSO .BR mosh (1), .BR mosh-client (1). Project home page: .I http://mosh.mit.edu .br .SH AUTHOR mosh was written by Keith Winstein . .SH BUGS Please report bugs to \fImosh-devel@mit.edu\fP. Users may also subscribe to the .nh .I mosh-users@mit.edu .hy mailing list, at .br .nh .I http://mailman.mit.edu/mailman/listinfo/mosh-users .hy . mosh-1.2.5/man/mosh.1000066400000000000000000000207771255051512600143070ustar00rootroot00000000000000.\" Hey, EMACS: -*- nroff -*- .\" First parameter, NAME, should be all caps .\" Second parameter, SECTION, should be 1-8, maybe w/ subsection .\" other parameters are allowed: see man(7), man(1) .TH MOSH 1 "April 2013" .\" Please adjust this date whenever revising the manpage. .\" .\" Some roff macros, for reference: .\" .nh disable hyphenation .\" .hy enable hyphenation .\" .ad l left justify .\" .ad b justify to both left and right margins .\" .nf disable filling .\" .fi enable filling .\" .br insert line break .\" .sp insert n+1 empty lines .\" for manpage-specific macros, see man(7) .SH NAME mosh \- mobile shell with roaming and intelligent local echo .SH SYNOPSIS .B mosh .RI [ options ] [--] [user@]host [command...] .br .SH DESCRIPTION \fBmosh\fP (mobile shell) is a remote terminal application that supports intermittent connectivity, allows roaming, and provides speculative local echo and line editing of user keystrokes. Compared with \fBssh\fP, \fBmosh\fP is more robust \(em its connections stay up across sleeps and changes in the client's IP address \(em and more responsive, because the protocol is tolerant of packet loss and the client can echo most keystrokes immediately, without waiting for a network round-trip. \fBmosh\fP uses \fBssh\fP to establish a connection to the remote host and authenticate with existing means (e.g., public-key authentication or a password). \fBmosh\fP executes the unprivileged \fBmosh-server\fP helper program on the server, then closes the SSH connection and starts the \fBmosh-client\fP, which establishes a long-lived datagram connection over UDP. To improve responsiveness, \fBmosh\fP runs a predictive model of the server's behavior in the background, trying to guess the effect of each keystroke on the screen. It makes predictions for normal typing, backspace, and the left- and right-arrow keys. When it is confident, \fBmosh\fP displays the predictions without waiting for the server. The predictive model must prove itself anew on each row of the terminal and after each control character, so \fBmosh\fP avoids echoing passwords or non-echoing editor commands. By default, \fBmosh\fP shows its predictions only on high-latency connections and to smooth out network glitches. (On longer-latency links, the predicted cells are underlined until confirmed by the server.) Occasional echo mistakes are corrected within a network round-trip and do not cause lasting effect. \fBmosh\fP does not support X forwarding or the non-interactive uses of SSH, including port forwarding or sshfs. \fBmosh\fP works through typical client-side network address translators but requires UDP to pass between client and server. By default, \fBmosh\fP uses the ports between 60000 and 61000, but allows the user to request a particular UDP port instead. \fBmosh\fP will do its best to arrange a UTF-8 character set locale on the client and server. The client must have locale-related environment variables that specify UTF-8. \fBmosh\fP will pass these client variables to the \fBmosh-server\fP on its command line, but in most cases they will not need to be used. \fBmosh-server\fP first attempts to use its own locale-related environment variables, which come from the system default configuration (sometimes /etc/default/locale) or from having been passed over the SSH connection. But if these variables don't call for the use of UTF-8, \fBmosh-server\fP will apply the locale-related environment variables from the client and try again. .SH OPTIONS .TP .B \fIcommand\fP Command to run on remote host. By default, \fBmosh\fP executes a login shell. .TP .B \-\-client=\fIPATH\fP path to client helper on local machine (default: "mosh-client") .TP .B \-\-server=\fICOMMAND\fP command to run server helper on remote machine (default: "mosh-server") The server helper is unprivileged and can be installed in the user's home directory. .TP .B \-\-ssh=\fICOMMAND\fP OpenSSH command to remotely execute mosh-server on remote machine (default: "ssh") An alternate ssh port can be specified with, \fIe.g.\fP, \-\-ssh="ssh \-p 2222". .TP .B \-\-predict=\fIWHEN\fP Controls use of speculative local echo. WHEN defaults to `adaptive' (show predictions on slower links and to smooth out network glitches) and can also be `always` or `never'. The MOSH_PREDICTION_DISPLAY environment variable controls this setting permanently and can adopt the same three values. Even on `always', \fBmosh\fP will only show predictions when it is confident. This generally means a previous prediction on the same row of the terminal has been confirmed by the server, without any intervening control character keystrokes. .TP .B \-a Synonym for \-\-predict=always .TP .B \-n Synonym for \-\-predict=never .TP .B --family=\fIFAMILY\fP Force the use of a particular address family, which defaults to `inet' (IPv4), and can also be `inet6' (IPv6; requires IO::Socket::IP or IO::Socket::INET6). .TP .B -4 Synonym for \-\-family=inet .TP .B -6 Synonym for \-\-family=inet6 .TP .B \-p \fIPORT\fP[:\fIPORT2\fP], \-\-port=\fIPORT\fP[:\fIPORT2\fP] Use a particular server-side UDP port or port range, for example, if this is the only port that is forwarded through a firewall to the server. Otherwise, \fBmosh\fP will choose a port between 60000 and 61000. .TP .B \-\-bind\-server={ssh|any|\fIIP\fP} Control the IP address that the \fBmosh-server\fP binds to. The default is `ssh', in which case the server will reply from the IP address that the SSH connection came from (as found in the \fBSSH_CONNECTION\fP environment variable). This is useful for multihomed servers. With \-\-bind\-server=any, the server will reply on the default interface and will not bind to a particular IP address. This can be useful if the connection is made through \fBsslh\fP or another tool that makes the SSH connection appear to come from localhost. With \-\-bind\-server=\fIIP\fP, the server will attempt to bind to the specified IP address. .TP .B \-\-no\-init Do not send the \fBsmcup\fP initialization string and \fBrmcup\fP deinitialization string to the client's terminal. On many terminals this disables alternate screen mode. .SH ESCAPE SEQUENCES The default escape character used by Mosh is ASCII RS (decimal 30). This is typically typed as \fBCtrl-^\fP or \fBCtrl-Shift-6\fP, on US English keyboards. Users of non-English keyboards may find it difficult or impossible to type the default escape character, and may need to change the escape character. See the description of MOSH_ESCAPE_KEY, below. In this description, the configured escape character is represented as \fBEsc\fP. There are two slightly different modes for escape sequences, depending whether the escape character is printable or not. If the escape character is a printable character, it must be prefixed with a newline, similar to \fBOpenSSH\fP. To send the escape character itself, type it twice. If the escape character is set to \fB~\fP, \fBmosh\fP will behave much like \fBOpenSSH\fP. If the escape character is a non-printable control character, no prefix is used and the escape character is recognized at any time. To send the escape character itself, type the escape character, then its corresponding ASCII character (for \fBCtrl-^\fP you would type \fB^\fP, for \fBCtrl-B\fP you would type \fBB\fP). The escape sequence to shut down the connection is \fBEsc .\fP. The sequence \fBEsc Ctrl-Z\fP suspends the client. Any other sequence passes both characters through to the server. .SH ENVIRONMENT VARIABLES These variables are not actually interpreted by .BR mosh (1) itself, but are passed through to .BR mosh-server (1). They are described here for ease of use. .TP .B MOSH_ESCAPE_KEY When set, this configures the escape character used for local commands. The escape character may be set to any ASCII character in the range 1-127. The variable must be set with a single literal ASCII character. Control characters are set with the actual ASCII control character, not with a printable representation such as "^B". .TP .B MOSH_PREDICTION_DISPLAY Controls local echo as described above. The command-line flag overrides this variable. .TP .B MOSH_TITLE_NOPREFIX When set, inhibits prepending "[mosh]" to window title. .SH SEE ALSO .BR mosh-client (1), .BR mosh-server (1). Project home page: .I http://mosh.mit.edu .br .SH AUTHOR mosh was written by Keith Winstein . .SH BUGS Please report bugs to \fImosh-devel@mit.edu\fP. Users may also subscribe to the .nh .I mosh-users@mit.edu .hy mailing list, at .br .nh .I http://mailman.mit.edu/mailman/listinfo/mosh-users .hy . mosh-1.2.5/ocb-license.html000066400000000000000000000041071255051512600155420ustar00rootroot00000000000000OCB - An Authenticated-Encryption Scheme - GPL Patent Grant - Rogaway

OCB: Patent Grant for GNU GPL

Whereas I, Phillip Rogaway (hereinafter "Inventor") have sought patent protection for certain technology (hereinafter "Patented Technology"), and Inventor wishes to aid the Free Software Foundation in achieving its goals, and Inventor wishes to increase public awareness of Patented Technology, Inventor hereby grants a fully paid-up, nonexclusive, royalty-free license to practice any patents claiming priority to the patent applications below ("the Patents") if practiced by software distributed under the terms of any version of the GNU General Public License as published by the Free Software Foundation, 59 Temple Place, Suite 330, Boston, MA 02111. Inventor reserves all other rights, including without limitation licensing for software not distributed under the GNU General Public License.

The patents:

  • 09/918,615 - Method and Apparatus for Facilitating Efficient Authenticated Encryption.
  • 09/948,084 - Method and Apparatus for Realizing a Parallelizable Variable-Input-Length Pseudorandom Function.

June 12, 2012: Phillip Rogaway licensed the distribution of OCB in Mosh under the GPL with the OpenSSL linking exception and iOS waiver contained in the COPYING.iOS file.

"Mosh with the two GPL exemptions you specify in the attached note is freely licensed to use for any OCB-related IP that I own."
mosh-1.2.5/scripts/000077500000000000000000000000001255051512600141565ustar00rootroot00000000000000mosh-1.2.5/scripts/Makefile.am000066400000000000000000000004351255051512600162140ustar00rootroot00000000000000EXTRA_DIST = wrap-compiler-for-flag-check mosh.pl if BUILD_CLIENT bin_SCRIPTS = mosh endif CLEANFILES = $(bin_SCRIPTS) mosh: mosh.pl ../VERSION Makefile @sed -e "s/\@VERSION\@/`cat ../VERSION`/" -e "s/\@PACKAGE_STRING\@/@PACKAGE_STRING@/" $(srcdir)/mosh.pl > mosh @chmod a+x mosh mosh-1.2.5/scripts/mosh.pl000077500000000000000000000232471255051512600154740ustar00rootroot00000000000000#!/usr/bin/env perl # Mosh: the mobile shell # Copyright 2012 Keith Winstein # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation, either version 3 of the License, or # (at your option) any later version. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program. If not, see . # # In addition, as a special exception, the copyright holders give # permission to link the code of portions of this program with the # OpenSSL library under certain conditions as described in each # individual source file, and distribute linked combinations including # the two. # # You must obey the GNU General Public License in all respects for all # of the code used other than OpenSSL. If you modify file(s) with this # exception, you may extend this exception to your version of the # file(s), but you are not obligated to do so. If you do not wish to do # so, delete this exception statement from your version. If you delete # this exception statement from all source files in the program, then # also delete it here. use warnings; use strict; use Getopt::Long; use IO::Socket; $|=1; my $client = 'mosh-client'; my $server = 'mosh-server'; my $predict = undef; my $bind_ip = undef; my $family = 'inet'; my $port_request = undef; my $ssh = 'ssh'; my $term_init = 1; my $help = undef; my $version = undef; my @cmdline = @ARGV; my $usage = qq{Usage: $0 [options] [--] [user@]host [command...] --client=PATH mosh client on local machine (default: "mosh-client") --server=COMMAND mosh server on remote machine (default: "mosh-server") --predict=adaptive local echo for slower links [default] -a --predict=always use local echo even on fast links -n --predict=never never use local echo --predict=experimental aggressively echo even when incorrect -4 --family=inet use IPv4 only [default] -6 --family=inet6 use IPv6 only -p PORT[:PORT2] --port=PORT[:PORT2] server-side UDP port or range --bind-server={ssh|any|IP} ask the server to reply from an IP address (default: "ssh") --ssh=COMMAND ssh command to run when setting up session (example: "ssh -p 2222") (default: "ssh") --no-init do not send terminal initialization string --help this message --version version and copyright information Please report bugs to mosh-devel\@mit.edu. Mosh home page: http://mosh.mit.edu\n}; my $version_message = '@PACKAGE_STRING@ [build @VERSION@]' . qq{ Copyright 2012 Keith Winstein License GPLv3+: GNU GPL version 3 or later . This is free software: you are free to change and redistribute it. There is NO WARRANTY, to the extent permitted by law.\n}; sub predict_check { my ( $predict, $env_set ) = @_; if ( not exists { adaptive => 0, always => 0, never => 0, experimental => 0 }->{ $predict } ) { my $explanation = $env_set ? " (MOSH_PREDICTION_DISPLAY in environment)" : ""; print STDERR qq{$0: Unknown mode \"$predict\"$explanation.\n\n}; die $usage; } } GetOptions( 'client=s' => \$client, 'server=s' => \$server, 'predict=s' => \$predict, 'port=s' => \$port_request, 'a' => sub { $predict = 'always' }, 'n' => sub { $predict = 'never' }, 'family=s' => \$family, '4' => sub { $family = 'inet' }, '6' => sub { $family = 'inet6' }, 'p=s' => \$port_request, 'ssh=s' => \$ssh, 'init!' => \$term_init, 'help' => \$help, 'version' => \$version, 'fake-proxy!' => \my $fake_proxy, 'bind-server=s' => \$bind_ip) or die $usage; die $usage if ( defined $help ); die $version_message if ( defined $version ); if ( defined $predict ) { predict_check( $predict, 0 ); } elsif ( defined $ENV{ 'MOSH_PREDICTION_DISPLAY' } ) { $predict = $ENV{ 'MOSH_PREDICTION_DISPLAY' }; predict_check( $predict, 1 ); } else { $predict = 'adaptive'; predict_check( $predict, 0 ); } if ( defined $port_request ) { if ( $port_request =~ m{^(\d+)(:(\d+))?$} ) { my ( $low, $clause, $high ) = ( $1, $2, $3 ); # good port or port-range if ( $low <= 0 or $low > 65535 ) { die "$0: Server-side (low) port ($low) must be within valid range [1..65535].\n"; } if ( defined $high ) { if ( $high <= 0 or $high > 65535 ) { die "$0: Server-side high port ($high) must be within valid range [1..65535].\n"; } if ( $low > $high ) { die "$0: Server-side port range ($port_request): low port greater than high port.\n"; } } } else { die "$0: Server-side port or range ($port_request) is not valid.\n"; } } delete $ENV{ 'MOSH_PREDICTION_DISPLAY' }; my @bind_arguments; if ( not defined $bind_ip or $bind_ip =~ m{^ssh$}i ) { push @bind_arguments, '-s'; } elsif ( $bind_ip =~ m{^any$}i ) { # do nothing } elsif ( $bind_ip =~ m{^[0-9\.]+$} ) { push @bind_arguments, ('-i', "$bind_ip"); } else { print STDERR qq{$0: Unknown server binding option: $bind_ip\n}; die $usage; } if ( defined $fake_proxy ) { use Errno qw(EINTR); my $have_ipv6 = eval { require IO::Socket::IP; IO::Socket::IP->import('-register'); 1; } || eval { require IO::Socket::INET6; 1; }; use POSIX qw(_exit); # Report failure if IPv6 needed and not available. if (lc($family) eq "inet6") { if (!$have_ipv6) { die "$0: IPv6 sockets not available in this Perl install\n"; } } my ( $host, $port ) = @ARGV; # Resolve hostname and connect my $afstr = 'AF_' . uc( $family ); my $af = eval { IO::Socket->$afstr } or die "$0: Invalid family $family\n"; my $sock = IO::Socket->new( Domain => $af, Family => $af, PeerHost => $host, PeerPort => $port, Proto => "tcp" ) or die "$0: Could not connect to $host: $@\n"; print STDERR "MOSH IP ", $sock->peerhost, "\n"; # Act like netcat binmode($sock); binmode(STDIN); binmode(STDOUT); sub cat { my ( $from, $to ) = @_; while ( my $n = $from->sysread( my $buf, 4096 ) ) { next if ( $n == -1 && $! == EINTR ); $n >= 0 or last; $to->write( $buf ) or last; } } defined( my $pid = fork ) or die "$0: fork: $!\n"; if ( $pid == 0 ) { close STDIN; cat $sock, \*STDOUT; $sock->shutdown( 0 ); _exit 0; } $SIG{ 'HUP' } = 'IGNORE'; close STDOUT; cat \*STDIN, $sock; $sock->shutdown( 1 ); close STDIN; waitpid $pid, 0; exit; } if ( scalar @ARGV < 1 ) { die $usage; } my $userhost = shift; my @command = @ARGV; # Count colors open COLORCOUNT, '-|', $client, ('-c') or die "Can't count colors: $!\n"; my $colors = ""; { local $/ = undef; $colors = ; } close COLORCOUNT or die; chomp $colors; if ( (not defined $colors) or $colors !~ m{^[0-9]+$} or $colors < 0 ) { $colors = 0; } my $pid = open(my $pipe, "-|"); die "$0: fork: $!\n" unless ( defined $pid ); if ( $pid == 0 ) { # child open(STDERR, ">&STDOUT") or die; my @server = ( 'new' ); push @server, ( '-c', $colors ); push @server, @bind_arguments; if ( defined $port_request ) { push @server, ( '-p', $port_request ); } for ( &locale_vars ) { push @server, ( '-l', $_ ); } if ( scalar @command > 0 ) { push @server, '--', @command; } my $quoted_self = shell_quote( $0, "--family=$family" ); exec "$ssh " . shell_quote( '-S', 'none', '-o', "ProxyCommand=$quoted_self --fake-proxy -- %h %p", '-n', '-tt', $userhost, '--', "$server " . shell_quote( @server ) ); die "Cannot exec ssh: $!\n"; } else { # parent my ( $ip, $port, $key ); my $bad_udp_port_warning = 0; LINE: while ( <$pipe> ) { chomp; if ( m{^MOSH IP } ) { if ( defined $ip ) { die "$0 error: detected attempt to redefine MOSH IP.\n"; } ( $ip ) = m{^MOSH IP (\S+)\s*$} or die "Bad MOSH IP string: $_\n"; } elsif ( m{^MOSH CONNECT } ) { if ( ( $port, $key ) = m{^MOSH CONNECT (\d+?) ([A-Za-z0-9/+]{22})\s*$} ) { last LINE; } else { die "Bad MOSH CONNECT string: $_\n"; } } else { if ( defined $port_request and $port_request =~ m{:} and m{Bad UDP port} ) { $bad_udp_port_warning = 1; } print "$_\n"; } } waitpid $pid, 0; close $pipe; if ( not defined $ip ) { die "$0: Did not find remote IP address (is SSH ProxyCommand disabled?).\n"; } if ( not defined $key or not defined $port ) { if ( $bad_udp_port_warning ) { die "$0: Server does not support UDP port range option.\n"; } die "$0: Did not find mosh server startup message.\n"; } # Now start real mosh client $ENV{ 'MOSH_KEY' } = $key; $ENV{ 'MOSH_PREDICTION_DISPLAY' } = $predict; $ENV{ 'MOSH_NO_TERM_INIT' } = '1' if !$term_init; exec {$client} ("$client @cmdline |", $ip, $port); } sub shell_quote { join ' ', map {(my $a = $_) =~ s/'/'\\''/g; "'$a'"} @_ } sub locale_vars { my @names = qw[LANG LANGUAGE LC_CTYPE LC_NUMERIC LC_TIME LC_COLLATE LC_MONETARY LC_MESSAGES LC_PAPER LC_NAME LC_ADDRESS LC_TELEPHONE LC_MEASUREMENT LC_IDENTIFICATION LC_ALL]; my @assignments; for ( @names ) { if ( defined $ENV{ $_ } ) { push @assignments, $_ . q{=} . $ENV{ $_ }; } } return @assignments; } mosh-1.2.5/scripts/wrap-compiler-for-flag-check000077500000000000000000000007661255051512600214440ustar00rootroot00000000000000#!/bin/sh # There is no way to make clang's "argument unused" warning fatal. So when # configure checks for supported flags, it runs $CC, $CXX, $LD via this # wrapper. # # Ideally the search string would also include 'clang: ' but this output might # depend on clang's argv[0]. if out=`"$@" 2>&1`; then echo "$out" if echo "$out" | grep 'warning: argument unused' >/dev/null; then echo "$0: found clang warning" exit 1 else exit 0 fi else code=$? echo "$out" exit $code fi mosh-1.2.5/src/000077500000000000000000000000001255051512600132565ustar00rootroot00000000000000mosh-1.2.5/src/Makefile.am000066400000000000000000000001231255051512600153060ustar00rootroot00000000000000SUBDIRS = protobufs util crypto terminal network statesync frontend examples tests mosh-1.2.5/src/crypto/000077500000000000000000000000001255051512600145765ustar00rootroot00000000000000mosh-1.2.5/src/crypto/Makefile.am000066400000000000000000000005011255051512600166260ustar00rootroot00000000000000AM_CPPFLAGS = -I$(srcdir)/../util $(OPENSSL_CFLAGS) AM_CXXFLAGS = $(WARNING_CXXFLAGS) $(PICKY_CXXFLAGS) $(HARDEN_CFLAGS) $(MISC_CXXFLAGS) noinst_LIBRARIES = libmoshcrypto.a OCB_SRCS = \ ae.h \ ocb.cc libmoshcrypto_a_SOURCES = \ $(OCB_SRCS) \ base64.cc \ base64.h \ byteorder.h \ crypto.cc \ crypto.h \ prng.h mosh-1.2.5/src/crypto/ae.h000066400000000000000000000170161255051512600153410ustar00rootroot00000000000000/* --------------------------------------------------------------------------- * * AEAD API 0.12 - 13 July 2011 * * This file gives an interface appropriate for many authenticated * encryption with associated data (AEAD) implementations. It does not try * to accommodate all possible options or limitations that an implementation * might have -- you should consult the documentation of your chosen * implementation to find things like RFC 5116 constants, alignment * requirements, whether the incremental interface is supported, etc. * * This file is in the public domain. It is provided "as is", without * warranty of any kind. Use at your own risk. * * Comments are welcome: Ted Krovetz . * * ------------------------------------------------------------------------ */ #ifndef _AE_H_ #define _AE_H_ #ifdef __cplusplus extern "C" { #endif /* -------------------------------------------------------------------------- * * Constants * * ----------------------------------------------------------------------- */ /* Return status codes: Negative return values indicate an error occurred. * For full explanations of error values, consult the implementation's * documentation. */ #define AE_SUCCESS ( 0) /* Indicates successful completion of call */ #define AE_INVALID (-1) /* Indicates bad tag during decryption */ #define AE_NOT_SUPPORTED (-2) /* Indicates unsupported option requested */ /* Flags: When data can be processed "incrementally", these flags are used * to indicate whether the submitted data is the last or not. */ #define AE_FINALIZE (1) /* This is the last of data */ #define AE_PENDING (0) /* More data of is coming */ /* -------------------------------------------------------------------------- * * AEAD opaque structure definition * * ----------------------------------------------------------------------- */ typedef struct _ae_ctx ae_ctx; /* -------------------------------------------------------------------------- * * Data Structure Routines * * ----------------------------------------------------------------------- */ ae_ctx* ae_allocate (void *misc); /* Allocate ae_ctx, set optional ptr */ void ae_free (ae_ctx *ctx); /* Deallocate ae_ctx struct */ int ae_clear (ae_ctx *ctx); /* Undo initialization */ int ae_ctx_sizeof(void); /* Return sizeof(ae_ctx) */ /* ae_allocate() allocates an ae_ctx structure, but does not initialize it. * ae_free() deallocates an ae_ctx structure, but does not zeroize it. * ae_clear() zeroes sensitive values associated with an ae_ctx structure * and deallocates any auxiliary structures allocated during ae_init(). * ae_ctx_sizeof() returns sizeof(ae_ctx), to aid in any static allocations. */ /* -------------------------------------------------------------------------- * * AEAD Routines * * ----------------------------------------------------------------------- */ int ae_init(ae_ctx *ctx, const void *key, int key_len, int nonce_len, int tag_len); /* -------------------------------------------------------------------------- * * Initialize an ae_ctx context structure. * * Parameters: * ctx - Pointer to an ae_ctx structure to be initialized * key - Pointer to user-supplied key * key_len - Length of key supplied, in bytes * nonce_len - Length of nonces to be used for this key, in bytes * tag_len - Length of tags to be produced for this key, in bytes * * Returns: * AE_SUCCESS - Success. Ctx ready for use. * AE_NOT_SUPPORTED - An unsupported length was supplied. Ctx is untouched. * Otherwise - Error. Check implementation documentation for codes. * * ----------------------------------------------------------------------- */ int ae_encrypt(ae_ctx *ctx, const void *nonce, const void *pt, int pt_len, const void *ad, int ad_len, void *ct, void *tag, int final); /* -------------------------------------------------------------------------- * * Encrypt plaintext; provide for authentication of ciphertext/associated data. * * Parameters: * ctx - Pointer to an ae_ctx structure initialized by ae_init. * nonce - Pointer to a nonce_len (defined in ae_init) byte nonce. * pt - Pointer to plaintext bytes to be encrypted. * pt_len - number of bytes pointed to by pt. * ad - Pointer to associated data. * ad_len - number of bytes pointed to by ad. * ct - Pointer to buffer to receive ciphertext encryption. * tag - Pointer to receive authentication tag; or NULL * if tag is to be bundled into the ciphertext. * final - Non-zero if this call completes the plaintext being encrypted. * * If nonce!=NULL then a message is being initiated. If final!=0 * then a message is being finalized. If final==0 or nonce==NULL * then the incremental interface is being used. If nonce!=NULL and * ad_len<0, then use same ad as last message. * * Returns: * non-negative - Number of bytes written to ct. * AE_NOT_SUPPORTED - Usage mode unsupported (eg, incremental and/or sticky). * Otherwise - Error. Check implementation documentation for codes. * * ----------------------------------------------------------------------- */ int ae_decrypt(ae_ctx *ctx, const void *nonce, const void *ct, int ct_len, const void *ad, int ad_len, void *pt, const void *tag, int final); /* -------------------------------------------------------------------------- * * Decrypt ciphertext; provide authenticity of plaintext and associated data. * * Parameters: * ctx - Pointer to an ae_ctx structure initialized by ae_init. * nonce - Pointer to a nonce_len (defined in ae_init) byte nonce. * ct - Pointer to ciphertext bytes to be decrypted. * ct_len - number of bytes pointed to by ct. * ad - Pointer to associated data. * ad_len - number of bytes pointed to by ad. * pt - Pointer to buffer to receive plaintext decryption. * tag - Pointer to tag_len (defined in ae_init) bytes; or NULL * if tag is bundled into the ciphertext. Non-NULL tag is only * read when final is non-zero. * final - Non-zero if this call completes the ciphertext being decrypted. * * If nonce!=NULL then "ct" points to the start of a ciphertext. If final!=0 * then "in" points to the final piece of ciphertext. If final==0 or nonce== * NULL then the incremental interface is being used. If nonce!=NULL and * ad_len<0, then use same ad as last message. * * Returns: * non-negative - Number of bytes written to pt. * AE_INVALID - Authentication failure. * AE_NOT_SUPPORTED - Usage mode unsupported (eg, incremental and/or sticky). * Otherwise - Error. Check implementation documentation for codes. * * NOTE !!! NOTE !!! -- The ciphertext should be assumed possibly inauthentic * until it has been completely written and it is * verified that this routine did not return AE_INVALID. * * ----------------------------------------------------------------------- */ #ifdef __cplusplus } /* closing brace for extern "C" */ #endif #endif /* _AE_H_ */ mosh-1.2.5/src/crypto/base64.cc000066400000000000000000000074751255051512600162060ustar00rootroot00000000000000/* Mosh: the mobile shell Copyright 2012 Keith Winstein This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . In addition, as a special exception, the copyright holders give permission to link the code of portions of this program with the OpenSSL library under certain conditions as described in each individual source file, and distribute linked combinations including the two. You must obey the GNU General Public License in all respects for all of the code used other than OpenSSL. If you modify file(s) with this exception, you may extend this exception to your version of the file(s), but you are not obligated to do so. If you do not wish to do so, delete this exception statement from your version. If you delete this exception statement from all source files in the program, then also delete it here. */ #include #include #include #include "fatal_assert.h" #include "base64.h" bool base64_decode( const char *b64, const size_t b64_len, char *raw, size_t *raw_len ) { bool ret = false; fatal_assert( b64_len == 24 ); /* only useful for Mosh keys */ fatal_assert( *raw_len == 16 ); /* initialize input/output */ BIO_METHOD *b64_method = BIO_f_base64(); fatal_assert( b64_method ); BIO *b64_bio = BIO_new( b64_method ); fatal_assert( b64_bio ); BIO_set_flags( b64_bio, BIO_FLAGS_BASE64_NO_NL ); BIO *mem_bio = BIO_new_mem_buf( (void *) b64, b64_len ); fatal_assert( mem_bio ); BIO *combined_bio = BIO_push( b64_bio, mem_bio ); fatal_assert( combined_bio ); fatal_assert( 1 == BIO_flush( combined_bio ) ); /* read the string */ int bytes_read = BIO_read( combined_bio, raw, *raw_len ); if ( bytes_read <= 0 ) { goto end; } if ( bytes_read != (int)*raw_len ) { goto end; } fatal_assert( 1 == BIO_flush( combined_bio ) ); /* check if there is more to read */ char extra[ 256 ]; bytes_read = BIO_read( combined_bio, extra, 256 ); if ( bytes_read > 0 ) { goto end; } /* check if mem buf is empty */ if ( !BIO_eof( mem_bio ) ) { goto end; } ret = true; end: BIO_free_all( combined_bio ); return ret; } void base64_encode( const char *raw, const size_t raw_len, char *b64, const size_t b64_len ) { fatal_assert( b64_len == 24 ); /* only useful for Mosh keys */ fatal_assert( raw_len == 16 ); /* initialize input/output */ BIO_METHOD *b64_method = BIO_f_base64(), *mem_method = BIO_s_mem(); fatal_assert( b64_method ); fatal_assert( mem_method ); BIO *b64_bio = BIO_new( b64_method ), *mem_bio = BIO_new( mem_method ); fatal_assert( b64_bio ); fatal_assert( mem_bio ); BIO_set_flags( b64_bio, BIO_FLAGS_BASE64_NO_NL ); BIO *combined_bio = BIO_push( b64_bio, mem_bio ); fatal_assert( combined_bio ); /* write the string */ int bytes_written = BIO_write( combined_bio, raw, raw_len ); fatal_assert( bytes_written >= 0 ); fatal_assert( bytes_written == (int)raw_len ); fatal_assert( 1 == BIO_flush( combined_bio ) ); /* check if mem buf has desired length */ fatal_assert( BIO_pending( mem_bio ) == (int)b64_len ); char *mem_ptr; BIO_get_mem_data( mem_bio, &mem_ptr ); memcpy( b64, mem_ptr, b64_len ); BIO_free_all( combined_bio ); } mosh-1.2.5/src/crypto/base64.h000066400000000000000000000032021255051512600160300ustar00rootroot00000000000000/* Mosh: the mobile shell Copyright 2012 Keith Winstein This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . In addition, as a special exception, the copyright holders give permission to link the code of portions of this program with the OpenSSL library under certain conditions as described in each individual source file, and distribute linked combinations including the two. You must obey the GNU General Public License in all respects for all of the code used other than OpenSSL. If you modify file(s) with this exception, you may extend this exception to your version of the file(s), but you are not obligated to do so. If you do not wish to do so, delete this exception statement from your version. If you delete this exception statement from all source files in the program, then also delete it here. */ bool base64_decode( const char *b64, const size_t b64_len, char *raw, size_t *raw_len ); void base64_encode( const char *raw, const size_t raw_len, char *b64, const size_t b64_len ); mosh-1.2.5/src/crypto/byteorder.h000066400000000000000000000067131255051512600167550ustar00rootroot00000000000000/* Mosh: the mobile shell Copyright 2012 Keith Winstein This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . In addition, as a special exception, the copyright holders give permission to link the code of portions of this program with the OpenSSL library under certain conditions as described in each individual source file, and distribute linked combinations including the two. You must obey the GNU General Public License in all respects for all of the code used other than OpenSSL. If you modify file(s) with this exception, you may extend this exception to your version of the file(s), but you are not obligated to do so. If you do not wish to do so, delete this exception statement from your version. If you delete this exception statement from all source files in the program, then also delete it here. */ #ifndef BYTEORDER_HPP #define BYTEORDER_HPP #include "config.h" #if HAVE_DECL_BE64TOH || HAVE_DECL_BETOH64 # if defined(HAVE_ENDIAN_H) # include # elif defined(HAVE_SYS_ENDIAN_H) # include # include # endif #if !HAVE_DECL_BE64TOH && HAVE_DECL_BETOH64 #define be64toh betoh64 #define be16toh betoh16 #endif #elif HAVE_OSX_SWAP # include # define htobe64 OSSwapHostToBigInt64 # define be64toh OSSwapBigToHostInt64 # define htobe16 OSSwapHostToBigInt16 # define be16toh OSSwapBigToHostInt16 #else /* Use our fallback implementation, which is correct for any endianness. */ #include /* Make sure they aren't macros */ #undef htobe64 #undef be64toh #undef htobe16 #undef be16toh /* Use unions rather than casts, to comply with strict aliasing rules. */ inline uint64_t htobe64( uint64_t x ) { uint8_t xs[ 8 ] = { ( x >> 56 ) & 0xFF, ( x >> 48 ) & 0xFF, ( x >> 40 ) & 0xFF, ( x >> 32 ) & 0xFF, ( x >> 24 ) & 0xFF, ( x >> 16 ) & 0xFF, ( x >> 8 ) & 0xFF, x & 0xFF }; union { const uint8_t *p8; const uint64_t *p64; } u; u.p8 = xs; return *u.p64; } inline uint64_t be64toh( uint64_t x ) { union { const uint8_t *p8; const uint64_t *p64; } u; u.p64 = &x; return ( uint64_t( u.p8[ 0 ] ) << 56 ) | ( uint64_t( u.p8[ 1 ] ) << 48 ) | ( uint64_t( u.p8[ 2 ] ) << 40 ) | ( uint64_t( u.p8[ 3 ] ) << 32 ) | ( uint64_t( u.p8[ 4 ] ) << 24 ) | ( uint64_t( u.p8[ 5 ] ) << 16 ) | ( uint64_t( u.p8[ 6 ] ) << 8 ) | ( uint64_t( u.p8[ 7 ] ) ); } inline uint16_t htobe16( uint16_t x ) { uint8_t xs[ 2 ] = { ( x >> 8 ) & 0xFF, x & 0xFF }; union { const uint8_t *p8; const uint16_t *p16; } u; u.p8 = xs; return *u.p16; } inline uint16_t be16toh( uint16_t x ) { union { const uint8_t *p8; const uint16_t *p16; } u; u.p16 = &x; return ( uint16_t( u.p8[ 0 ] ) << 8 ) | ( uint16_t( u.p8[ 1 ] ) ); } #endif #endif mosh-1.2.5/src/crypto/crypto.cc000066400000000000000000000216771255051512600164420ustar00rootroot00000000000000/* Mosh: the mobile shell Copyright 2012 Keith Winstein This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . In addition, as a special exception, the copyright holders give permission to link the code of portions of this program with the OpenSSL library under certain conditions as described in each individual source file, and distribute linked combinations including the two. You must obey the GNU General Public License in all respects for all of the code used other than OpenSSL. If you modify file(s) with this exception, you may extend this exception to your version of the file(s), but you are not obligated to do so. If you do not wish to do so, delete this exception statement from your version. If you delete this exception statement from all source files in the program, then also delete it here. */ #include #include #include #include #include #include #include #include "byteorder.h" #include "crypto.h" #include "base64.h" #include "prng.h" using namespace std; using namespace Crypto; long int myatoi( const char *str ) { char *end; errno = 0; long int ret = strtol( str, &end, 10 ); if ( ( errno != 0 ) || ( end != str + strlen( str ) ) ) { throw CryptoException( "Bad integer." ); } return ret; } AlignedBuffer::AlignedBuffer( size_t len, const char *data ) : m_len( len ), m_allocated( NULL ), m_data( NULL ) { #if defined(HAVE_POSIX_MEMALIGN) if ( ( 0 != posix_memalign( &m_allocated, 16, len ) ) || ( m_allocated == NULL ) ) { throw std::bad_alloc(); } m_data = (char *) m_allocated; #else /* malloc() a region 15 bytes larger than we need, and find the aligned offset within. */ m_allocated = malloc( 15 + len ); if ( m_allocated == NULL ) { throw std::bad_alloc(); } uintptr_t iptr = (uintptr_t) m_allocated; if ( iptr & 0xF ) { iptr += 16 - ( iptr & 0xF ); } assert( !( iptr & 0xF ) ); assert( iptr >= (uintptr_t) m_allocated ); assert( iptr <= ( 15 + (uintptr_t) m_allocated ) ); m_data = (char *) iptr; #endif /* !defined(HAVE_POSIX_MEMALIGN) */ if ( data ) { memcpy( m_data, data, len ); } } Base64Key::Base64Key( string printable_key ) { if ( printable_key.length() != 22 ) { throw CryptoException( "Key must be 22 letters long." ); } string base64 = printable_key + "=="; size_t len = 16; if ( !base64_decode( base64.data(), 24, (char *)&key[ 0 ], &len ) ) { throw CryptoException( "Key must be well-formed base64." ); } if ( len != 16 ) { throw CryptoException( "Key must represent 16 octets." ); } /* to catch changes after the first 128 bits */ if ( printable_key != this->printable_key() ) { throw CryptoException( "Base64 key was not encoded 128-bit key." ); } } Base64Key::Base64Key() { PRNG().fill( key, sizeof( key ) ); } string Base64Key::printable_key( void ) const { char base64[ 24 ]; base64_encode( (char *)key, 16, base64, 24 ); if ( (base64[ 23 ] != '=') || (base64[ 22 ] != '=') ) { throw CryptoException( string( "Unexpected output from base64_encode: " ) + string( base64, 24 ) ); } base64[ 22 ] = 0; return string( base64 ); } Session::Session( Base64Key s_key ) : key( s_key ), ctx_buf( ae_ctx_sizeof() ), ctx( (ae_ctx *)ctx_buf.data() ), blocks_encrypted( 0 ), plaintext_buffer( RECEIVE_MTU ), ciphertext_buffer( RECEIVE_MTU ), nonce_buffer( Nonce::NONCE_LEN ) { if ( AE_SUCCESS != ae_init( ctx, key.data(), 16, 12, 16 ) ) { throw CryptoException( "Could not initialize AES-OCB context." ); } } Session::~Session() { if ( ae_clear( ctx ) != AE_SUCCESS ) { throw CryptoException( "Could not clear AES-OCB context." ); } } Nonce::Nonce( uint64_t val ) { uint64_t val_net = htobe64( val ); memset( bytes, 0, 4 ); memcpy( bytes + 4, &val_net, 8 ); } uint64_t Nonce::val( void ) { uint64_t ret; memcpy( &ret, bytes + 4, 8 ); return be64toh( ret ); } Nonce::Nonce( char *s_bytes, size_t len ) { if ( len != 8 ) { throw CryptoException( "Nonce representation must be 8 octets long." ); } memset( bytes, 0, 4 ); memcpy( bytes + 4, s_bytes, 8 ); } Message::Message( char *nonce_bytes, size_t nonce_len, char *text_bytes, size_t text_len ) : nonce( nonce_bytes, nonce_len ), text( (char *)text_bytes, text_len ) {} Message::Message( Nonce s_nonce, string s_text ) : nonce( s_nonce ), text( s_text ) {} string Session::encrypt( Message plaintext ) { const size_t pt_len = plaintext.text.size(); const int ciphertext_len = pt_len + 16; assert( (size_t)ciphertext_len <= ciphertext_buffer.len() ); assert( pt_len <= plaintext_buffer.len() ); memcpy( plaintext_buffer.data(), plaintext.text.data(), pt_len ); memcpy( nonce_buffer.data(), plaintext.nonce.data(), Nonce::NONCE_LEN ); if ( ciphertext_len != ae_encrypt( ctx, /* ctx */ nonce_buffer.data(), /* nonce */ plaintext_buffer.data(), /* pt */ pt_len, /* pt_len */ NULL, /* ad */ 0, /* ad_len */ ciphertext_buffer.data(), /* ct */ NULL, /* tag */ AE_FINALIZE ) ) { /* final */ throw CryptoException( "ae_encrypt() returned error." ); } blocks_encrypted += pt_len >> 4; if ( pt_len & 0xF ) { /* partial block */ blocks_encrypted++; } /* "Both the privacy and the authenticity properties of OCB degrade as per s^2 / 2^128, where s is the total number of blocks that the adversary acquires.... In order to ensure that s^2 / 2^128 remains small, a given key should be used to encrypt at most 2^48 blocks (2^55 bits or 4 petabytes)" -- http://tools.ietf.org/html/draft-krovetz-ocb-03 We deem it unlikely that a legitimate user will send 4 PB through a Mosh session. If it happens, we simply kill the session. The server and client use the same key, so we actually need to die after 2^47 blocks. */ if ( blocks_encrypted >> 47 ) { throw CryptoException( "Encrypted 2^47 blocks.", true ); } string text( ciphertext_buffer.data(), ciphertext_len ); return plaintext.nonce.cc_str() + text; } Message Session::decrypt( string ciphertext ) { if ( ciphertext.size() < 24 ) { throw CryptoException( "Ciphertext must contain nonce and tag." ); } char *str = (char *)ciphertext.data(); int body_len = ciphertext.size() - 8; int pt_len = body_len - 16; if ( pt_len < 0 ) { /* super-assertion that pt_len does not equal AE_INVALID */ fprintf( stderr, "BUG.\n" ); exit( 1 ); } assert( (size_t)body_len <= ciphertext_buffer.len() ); assert( (size_t)pt_len <= plaintext_buffer.len() ); Nonce nonce( str, 8 ); memcpy( ciphertext_buffer.data(), str + 8, body_len ); memcpy( nonce_buffer.data(), nonce.data(), Nonce::NONCE_LEN ); if ( pt_len != ae_decrypt( ctx, /* ctx */ nonce_buffer.data(), /* nonce */ ciphertext_buffer.data(), /* ct */ body_len, /* ct_len */ NULL, /* ad */ 0, /* ad_len */ plaintext_buffer.data(), /* pt */ NULL, /* tag */ AE_FINALIZE ) ) { /* final */ throw CryptoException( "Packet failed integrity check." ); } Message ret( nonce, string( plaintext_buffer.data(), pt_len ) ); return ret; } static rlim_t saved_core_rlimit; /* Disable dumping core, as a precaution to avoid saving sensitive data to disk. */ void Crypto::disable_dumping_core( void ) { struct rlimit limit; if ( 0 != getrlimit( RLIMIT_CORE, &limit ) ) { /* We don't throw CryptoException because this is called very early in main(), outside of 'try'. */ perror( "getrlimit(RLIMIT_CORE)" ); exit( 1 ); } saved_core_rlimit = limit.rlim_cur; limit.rlim_cur = 0; if ( 0 != setrlimit( RLIMIT_CORE, &limit ) ) { perror( "setrlimit(RLIMIT_CORE)" ); exit( 1 ); } } void Crypto::reenable_dumping_core( void ) { /* Silent failure is safe. */ struct rlimit limit; if ( 0 == getrlimit( RLIMIT_CORE, &limit ) ) { limit.rlim_cur = saved_core_rlimit; setrlimit( RLIMIT_CORE, &limit ); } } mosh-1.2.5/src/crypto/crypto.h000066400000000000000000000074701255051512600162770ustar00rootroot00000000000000/* Mosh: the mobile shell Copyright 2012 Keith Winstein This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . In addition, as a special exception, the copyright holders give permission to link the code of portions of this program with the OpenSSL library under certain conditions as described in each individual source file, and distribute linked combinations including the two. You must obey the GNU General Public License in all respects for all of the code used other than OpenSSL. If you modify file(s) with this exception, you may extend this exception to your version of the file(s), but you are not obligated to do so. If you do not wish to do so, delete this exception statement from your version. If you delete this exception statement from all source files in the program, then also delete it here. */ #ifndef CRYPTO_HPP #define CRYPTO_HPP #include "ae.h" #include #include #include #include #include using std::string; long int myatoi( const char *str ); namespace Crypto { class CryptoException : public std::exception { public: string text; bool fatal; CryptoException( string s_text, bool s_fatal = false ) : text( s_text ), fatal( s_fatal ) {}; const char *what() const throw () { return text.c_str(); } ~CryptoException() throw () {} }; /* 16-byte-aligned buffer, with length. */ class AlignedBuffer { private: size_t m_len; void *m_allocated; char *m_data; public: AlignedBuffer( size_t len, const char *data = NULL ); ~AlignedBuffer() { free( m_allocated ); } char * data( void ) const { return m_data; } size_t len( void ) const { return m_len; } private: /* Not implemented */ AlignedBuffer( const AlignedBuffer & ); AlignedBuffer & operator=( const AlignedBuffer & ); }; class Base64Key { private: unsigned char key[ 16 ]; public: Base64Key(); /* random key */ Base64Key( string printable_key ); string printable_key( void ) const; unsigned char *data( void ) { return key; } }; class Nonce { public: static const int NONCE_LEN = 12; private: char bytes[ NONCE_LEN ]; public: Nonce( uint64_t val ); Nonce( char *s_bytes, size_t len ); string cc_str( void ) const { return string( bytes + 4, 8 ); } const char *data( void ) const { return bytes; } uint64_t val( void ); }; class Message { public: Nonce nonce; string text; Message( char *nonce_bytes, size_t nonce_len, char *text_bytes, size_t text_len ); Message( Nonce s_nonce, string s_text ); }; class Session { private: Base64Key key; AlignedBuffer ctx_buf; ae_ctx *ctx; uint64_t blocks_encrypted; AlignedBuffer plaintext_buffer; AlignedBuffer ciphertext_buffer; AlignedBuffer nonce_buffer; public: static const int RECEIVE_MTU = 2048; Session( Base64Key s_key ); ~Session(); string encrypt( Message plaintext ); Message decrypt( string ciphertext ); Session( const Session & ); Session & operator=( const Session & ); }; void disable_dumping_core( void ); void reenable_dumping_core( void ); } #endif mosh-1.2.5/src/crypto/ocb.cc000066400000000000000000001422751255051512600156630ustar00rootroot00000000000000/*------------------------------------------------------------------------ / OCB Version 3 Reference Code (Optimized C) Last modified 08-SEP-2012 /------------------------------------------------------------------------- / Copyright (c) 2012 Ted Krovetz. / / Permission to use, copy, modify, and/or distribute this software for any / purpose with or without fee is hereby granted, provided that the above / copyright notice and this permission notice appear in all copies. / / THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES / WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF / MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR / ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES / WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN / ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF / OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. / / Phillip Rogaway holds patents relevant to OCB. See the following for / his patent grant: http://www.cs.ucdavis.edu/~rogaway/ocb/grant.htm / / Special thanks to Keegan McAllister for suggesting several good improvements / / Comments are welcome: Ted Krovetz - Dedicated to Laurel K /------------------------------------------------------------------------- */ /* ----------------------------------------------------------------------- */ /* Usage notes */ /* ----------------------------------------------------------------------- */ /* - When AE_PENDING is passed as the 'final' parameter of any function, / the length parameters must be a multiple of (BPI*16). / - When available, SSE or AltiVec registers are used to manipulate data. / So, when on machines with these facilities, all pointers passed to / any function should be 16-byte aligned. / - Plaintext and ciphertext pointers may be equal (ie, plaintext gets / encrypted in-place), but no other pair of pointers may be equal. / - This code assumes all x86 processors have SSE2 and SSSE3 instructions / when compiling under MSVC. If untrue, alter the #define. / - This code is tested for C99 and recent versions of GCC and MSVC. */ /* ----------------------------------------------------------------------- */ /* User configuration options */ /* ----------------------------------------------------------------------- */ /* Set the AES key length to use and length of authentication tag to produce. / Setting either to 0 requires the value be set at runtime via ae_init(). / Some optimizations occur for each when set to a fixed value. */ #define OCB_KEY_LEN 16 /* 0, 16, 24 or 32. 0 means set in ae_init */ #define OCB_TAG_LEN 16 /* 0 to 16. 0 means set in ae_init */ /* This implementation has built-in support for multiple AES APIs. Set any / one of the following to non-zero to specify which to use. */ #define USE_OPENSSL_AES 1 /* http://openssl.org */ #define USE_REFERENCE_AES 0 /* Internet search: rijndael-alg-fst.c */ #define USE_AES_NI 0 /* Uses compiler's intrinsics */ /* During encryption and decryption, various "L values" are required. / The L values can be precomputed during initialization (requiring extra / space in ae_ctx), generated as needed (slightly slowing encryption and / decryption), or some combination of the two. L_TABLE_SZ specifies how many / L values to precompute. L_TABLE_SZ must be at least 3. L_TABLE_SZ*16 bytes / are used for L values in ae_ctx. Plaintext and ciphertexts shorter than / 2^L_TABLE_SZ blocks need no L values calculated dynamically. */ #define L_TABLE_SZ 16 /* Set L_TABLE_SZ_IS_ENOUGH non-zero iff you know that all plaintexts / will be shorter than 2^(L_TABLE_SZ+4) bytes in length. This results / in better performance. */ #define L_TABLE_SZ_IS_ENOUGH 1 /* ----------------------------------------------------------------------- */ /* Includes and compiler specific definitions */ /* ----------------------------------------------------------------------- */ #include "ae.h" #include #include /* Define standard sized integers */ #if defined(_MSC_VER) && (_MSC_VER < 1600) typedef unsigned __int8 uint8_t; typedef unsigned __int32 uint32_t; typedef unsigned __int64 uint64_t; typedef __int64 int64_t; #else #include #endif /* Compiler-specific intrinsics and fixes: bswap64, ntz */ #if _MSC_VER #define inline __inline /* MSVC doesn't recognize "inline" in C */ #define restrict __restrict /* MSVC doesn't recognize "restrict" in C */ #define __SSE2__ (_M_IX86 || _M_AMD64 || _M_X64) /* Assume SSE2 */ #define __SSSE3__ (_M_IX86 || _M_AMD64 || _M_X64) /* Assume SSSE3 */ #include #pragma intrinsic(_byteswap_uint64, _BitScanForward, memcpy) #elif __GNUC__ #define inline __inline__ /* No "inline" in GCC ansi C mode */ #define restrict __restrict__ /* No "restrict" in GCC ansi C mode */ #endif #if _MSC_VER #define bswap64(x) _byteswap_uint64(x) #elif HAVE_DECL___BUILTIN_BSWAP64 #define bswap64(x) __builtin_bswap64(x) /* GCC 4.3+ */ #else #define bswap32(x) \ ((((x) & 0xff000000u) >> 24) | (((x) & 0x00ff0000u) >> 8) | \ (((x) & 0x0000ff00u) << 8) | (((x) & 0x000000ffu) << 24)) static inline uint64_t bswap64(uint64_t x) { union { uint64_t u64; uint32_t u32[2]; } in, out; in.u64 = x; out.u32[0] = bswap32(in.u32[1]); out.u32[1] = bswap32(in.u32[0]); return out.u64; } #endif #if _MSC_VER static inline unsigned ntz(unsigned x) {_BitScanForward(&x,x);return x;} #elif HAVE_DECL___BUILTIN_CTZ #define ntz(x) __builtin_ctz((unsigned)(x)) /* GCC 3.4+ */ #else #if (L_TABLE_SZ <= 9) && (L_TABLE_SZ_IS_ENOUGH) /* < 2^13 byte texts */ static inline unsigned ntz(unsigned x) { static const unsigned char tz_table[] = {0, 2,3,2,4,2,3,2,5,2,3,2,4,2,3,2,6,2,3,2,4,2,3,2,5,2,3,2,4,2,3,2,7, 2,3,2,4,2,3,2,5,2,3,2,4,2,3,2,6,2,3,2,4,2,3,2,5,2,3,2,4,2,3,2,8, 2,3,2,4,2,3,2,5,2,3,2,4,2,3,2,6,2,3,2,4,2,3,2,5,2,3,2,4,2,3,2,7, 2,3,2,4,2,3,2,5,2,3,2,4,2,3,2,6,2,3,2,4,2,3,2,5,2,3,2,4,2,3,2}; return tz_table[x/4]; } #else /* From http://supertech.csail.mit.edu/papers/debruijn.pdf */ static inline unsigned ntz(unsigned x) { static const unsigned char tz_table[32] = { 0, 1, 28, 2, 29, 14, 24, 3, 30, 22, 20, 15, 25, 17, 4, 8, 31, 27, 13, 23, 21, 19, 16, 7, 26, 12, 18, 6, 11, 5, 10, 9}; return tz_table[((uint32_t)((x & -x) * 0x077CB531u)) >> 27]; } #endif #endif /* ----------------------------------------------------------------------- */ /* Define blocks and operations -- Patch if incorrect on your compiler. */ /* ----------------------------------------------------------------------- */ #if __SSE2__ #include /* SSE instructions and _mm_malloc */ #include /* SSE2 instructions */ typedef __m128i block; #define xor_block(x,y) _mm_xor_si128(x,y) #define zero_block() _mm_setzero_si128() #define unequal_blocks(x,y) \ (_mm_movemask_epi8(_mm_cmpeq_epi8(x,y)) != 0xffff) #if __SSSE3__ || USE_AES_NI #include /* SSSE3 instructions */ #define swap_if_le(b) \ _mm_shuffle_epi8(b,_mm_set_epi8(0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15)) #else static inline block swap_if_le(block b) { block a = _mm_shuffle_epi32 (b, _MM_SHUFFLE(0,1,2,3)); a = _mm_shufflehi_epi16(a, _MM_SHUFFLE(2,3,0,1)); a = _mm_shufflelo_epi16(a, _MM_SHUFFLE(2,3,0,1)); return _mm_xor_si128(_mm_srli_epi16(a,8), _mm_slli_epi16(a,8)); } #endif static inline block gen_offset(uint64_t KtopStr[3], unsigned bot) { block hi = _mm_load_si128((__m128i *)(KtopStr+0)); /* hi = B A */ block lo = _mm_loadu_si128((__m128i *)(KtopStr+1)); /* lo = C B */ __m128i lshift = _mm_cvtsi32_si128(bot); __m128i rshift = _mm_cvtsi32_si128(64-bot); lo = _mm_xor_si128(_mm_sll_epi64(hi,lshift),_mm_srl_epi64(lo,rshift)); #if __SSSE3__ || USE_AES_NI return _mm_shuffle_epi8(lo,_mm_set_epi8(8,9,10,11,12,13,14,15,0,1,2,3,4,5,6,7)); #else return swap_if_le(_mm_shuffle_epi32(lo, _MM_SHUFFLE(1,0,3,2))); #endif } static inline block double_block(block bl) { const __m128i mask = _mm_set_epi32(135,1,1,1); __m128i tmp = _mm_srai_epi32(bl, 31); tmp = _mm_and_si128(tmp, mask); tmp = _mm_shuffle_epi32(tmp, _MM_SHUFFLE(2,1,0,3)); bl = _mm_slli_epi32(bl, 1); return _mm_xor_si128(bl,tmp); } #elif __ALTIVEC__ && _CALL_ELF != 2 #include typedef vector unsigned block; #define xor_block(x,y) vec_xor(x,y) #define zero_block() vec_splat_u32(0) #define unequal_blocks(x,y) vec_any_ne(x,y) #define swap_if_le(b) (b) #if __PPC64__ static block gen_offset(uint64_t KtopStr[3], unsigned bot) { union {uint64_t u64[2]; block bl;} rval; rval.u64[0] = (KtopStr[0] << bot) | (KtopStr[1] >> (64-bot)); rval.u64[1] = (KtopStr[1] << bot) | (KtopStr[2] >> (64-bot)); return rval.bl; } #else /* Special handling: Shifts are mod 32, and no 64-bit types */ static block gen_offset(uint64_t KtopStr[3], unsigned bot) { const vector unsigned k32 = {32,32,32,32}; vector unsigned hi = *(vector unsigned *)(KtopStr+0); vector unsigned lo = *(vector unsigned *)(KtopStr+2); vector unsigned bot_vec; if (bot < 32) { lo = vec_sld(hi,lo,4); } else { vector unsigned t = vec_sld(hi,lo,4); lo = vec_sld(hi,lo,8); hi = t; bot = bot - 32; } if (bot == 0) return hi; *(unsigned *)&bot_vec = bot; vector unsigned lshift = vec_splat(bot_vec,0); vector unsigned rshift = vec_sub(k32,lshift); hi = vec_sl(hi,lshift); lo = vec_sr(lo,rshift); return vec_xor(hi,lo); } #endif static inline block double_block(block b) { const vector unsigned char mask = {135,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1}; const vector unsigned char perm = {1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,0}; const vector unsigned char shift7 = vec_splat_u8(7); const vector unsigned char shift1 = vec_splat_u8(1); vector unsigned char c = (vector unsigned char)b; vector unsigned char t = vec_sra(c,shift7); t = vec_and(t,mask); t = vec_perm(t,t,perm); c = vec_sl(c,shift1); return (block)vec_xor(c,t); } #elif __ARM_NEON__ #include typedef int8x16_t block; /* Yay! Endian-neutral reads! */ #define xor_block(x,y) veorq_s8(x,y) #define zero_block() vdupq_n_s8(0) static inline int unequal_blocks(block a, block b) { int64x2_t t=veorq_s64((int64x2_t)a,(int64x2_t)b); return (vgetq_lane_s64(t,0)|vgetq_lane_s64(t,1))!=0; } #define swap_if_le(b) (b) /* Using endian-neutral int8x16_t */ /* KtopStr is reg correct by 64 bits, return mem correct */ static block gen_offset(uint64_t KtopStr[3], unsigned bot) { const union { unsigned x; unsigned char endian; } little = { 1 }; const int64x2_t k64 = {-64,-64}; uint64x2_t hi = *(uint64x2_t *)(KtopStr+0); /* hi = A B */ uint64x2_t lo = *(uint64x2_t *)(KtopStr+1); /* hi = B C */ int64x2_t ls = vdupq_n_s64(bot); int64x2_t rs = vqaddq_s64(k64,ls); block rval = (block)veorq_u64(vshlq_u64(hi,ls),vshlq_u64(lo,rs)); if (little.endian) rval = vrev64q_s8(rval); return rval; } static inline block double_block(block b) { const block mask = {-121,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1}; block tmp = vshrq_n_s8(b,7); tmp = vandq_s8(tmp, mask); tmp = vextq_s8(tmp, tmp, 1); /* Rotate high byte to end */ b = vshlq_n_s8(b,1); return veorq_s8(tmp,b); } #else typedef struct { uint64_t l,r; } block; static inline block xor_block(block x, block y) { x.l^=y.l; x.r^=y.r; return x; } static inline block zero_block(void) { const block t = {0,0}; return t; } #define unequal_blocks(x, y) ((((x).l^(y).l)|((x).r^(y).r)) != 0) static inline block swap_if_le(block b) { const union { unsigned x; unsigned char endian; } little = { 1 }; if (little.endian) { block r; r.l = bswap64(b.l); r.r = bswap64(b.r); return r; } else return b; } /* KtopStr is reg correct by 64 bits, return mem correct */ static block gen_offset(uint64_t KtopStr[3], unsigned bot) { block rval; if (bot != 0) { rval.l = (KtopStr[0] << bot) | (KtopStr[1] >> (64-bot)); rval.r = (KtopStr[1] << bot) | (KtopStr[2] >> (64-bot)); } else { rval.l = KtopStr[0]; rval.r = KtopStr[1]; } return swap_if_le(rval); } #if __GNUC__ && !__clang__ && __arm__ static inline block double_block(block b) { __asm__ ("adds %1,%1,%1\n\t" "adcs %H1,%H1,%H1\n\t" "adcs %0,%0,%0\n\t" "adcs %H0,%H0,%H0\n\t" "it cs\n\t" "eorcs %1,%1,#135" : "+r"(b.l), "+r"(b.r) : : "cc"); return b; } #else static inline block double_block(block b) { uint64_t t = (uint64_t)((int64_t)b.l >> 63); b.l = (b.l + b.l) ^ (b.r >> 63); b.r = (b.r + b.r) ^ (t & 135); return b; } #endif #endif /* ----------------------------------------------------------------------- */ /* AES - Code uses OpenSSL API. Other implementations get mapped to it. */ /* ----------------------------------------------------------------------- */ /*---------------*/ #if USE_OPENSSL_AES /*---------------*/ #include /* http://openssl.org/ */ /* How to ECB encrypt an array of blocks, in place */ static inline void AES_ecb_encrypt_blks(block *blks, unsigned nblks, AES_KEY *key) { while (nblks) { --nblks; AES_encrypt((unsigned char *)(blks+nblks), (unsigned char *)(blks+nblks), key); } } static inline void AES_ecb_decrypt_blks(block *blks, unsigned nblks, AES_KEY *key) { while (nblks) { --nblks; AES_decrypt((unsigned char *)(blks+nblks), (unsigned char *)(blks+nblks), key); } } #define BPI 4 /* Number of blocks in buffer per ECB call */ /*-------------------*/ #elif USE_REFERENCE_AES /*-------------------*/ #include "rijndael-alg-fst.h" /* Barreto's Public-Domain Code */ #if (OCB_KEY_LEN == 0) typedef struct { uint32_t rd_key[60]; int rounds; } AES_KEY; #define ROUNDS(ctx) ((ctx)->rounds) #define AES_set_encrypt_key(x, y, z) \ do {rijndaelKeySetupEnc((z)->rd_key, x, y); (z)->rounds = y/32+6;} while (0) #define AES_set_decrypt_key(x, y, z) \ do {rijndaelKeySetupDec((z)->rd_key, x, y); (z)->rounds = y/32+6;} while (0) #else typedef struct { uint32_t rd_key[OCB_KEY_LEN+28]; } AES_KEY; #define ROUNDS(ctx) (6+OCB_KEY_LEN/4) #define AES_set_encrypt_key(x, y, z) rijndaelKeySetupEnc((z)->rd_key, x, y) #define AES_set_decrypt_key(x, y, z) rijndaelKeySetupDec((z)->rd_key, x, y) #endif #define AES_encrypt(x,y,z) rijndaelEncrypt((z)->rd_key, ROUNDS(z), x, y) #define AES_decrypt(x,y,z) rijndaelDecrypt((z)->rd_key, ROUNDS(z), x, y) static void AES_ecb_encrypt_blks(block *blks, unsigned nblks, AES_KEY *key) { while (nblks) { --nblks; AES_encrypt((unsigned char *)(blks+nblks), (unsigned char *)(blks+nblks), key); } } void AES_ecb_decrypt_blks(block *blks, unsigned nblks, AES_KEY *key) { while (nblks) { --nblks; AES_decrypt((unsigned char *)(blks+nblks), (unsigned char *)(blks+nblks), key); } } #define BPI 4 /* Number of blocks in buffer per ECB call */ /*----------*/ #elif USE_AES_NI /*----------*/ #include #if (OCB_KEY_LEN == 0) typedef struct { __m128i rd_key[15]; int rounds; } AES_KEY; #define ROUNDS(ctx) ((ctx)->rounds) #else typedef struct { __m128i rd_key[7+OCB_KEY_LEN/4]; } AES_KEY; #define ROUNDS(ctx) (6+OCB_KEY_LEN/4) #endif #define EXPAND_ASSIST(v1,v2,v3,v4,shuff_const,aes_const) \ v2 = _mm_aeskeygenassist_si128(v4,aes_const); \ v3 = _mm_castps_si128(_mm_shuffle_ps(_mm_castsi128_ps(v3), \ _mm_castsi128_ps(v1), 16)); \ v1 = _mm_xor_si128(v1,v3); \ v3 = _mm_castps_si128(_mm_shuffle_ps(_mm_castsi128_ps(v3), \ _mm_castsi128_ps(v1), 140)); \ v1 = _mm_xor_si128(v1,v3); \ v2 = _mm_shuffle_epi32(v2,shuff_const); \ v1 = _mm_xor_si128(v1,v2) #define EXPAND192_STEP(idx,aes_const) \ EXPAND_ASSIST(x0,x1,x2,x3,85,aes_const); \ x3 = _mm_xor_si128(x3,_mm_slli_si128 (x3, 4)); \ x3 = _mm_xor_si128(x3,_mm_shuffle_epi32(x0, 255)); \ kp[idx] = _mm_castps_si128(_mm_shuffle_ps(_mm_castsi128_ps(tmp), \ _mm_castsi128_ps(x0), 68)); \ kp[idx+1] = _mm_castps_si128(_mm_shuffle_ps(_mm_castsi128_ps(x0), \ _mm_castsi128_ps(x3), 78)); \ EXPAND_ASSIST(x0,x1,x2,x3,85,(aes_const*2)); \ x3 = _mm_xor_si128(x3,_mm_slli_si128 (x3, 4)); \ x3 = _mm_xor_si128(x3,_mm_shuffle_epi32(x0, 255)); \ kp[idx+2] = x0; tmp = x3 static void AES_128_Key_Expansion(const unsigned char *userkey, void *key) { __m128i x0,x1,x2; __m128i *kp = (__m128i *)key; kp[0] = x0 = _mm_loadu_si128((__m128i*)userkey); x2 = _mm_setzero_si128(); EXPAND_ASSIST(x0,x1,x2,x0,255,1); kp[1] = x0; EXPAND_ASSIST(x0,x1,x2,x0,255,2); kp[2] = x0; EXPAND_ASSIST(x0,x1,x2,x0,255,4); kp[3] = x0; EXPAND_ASSIST(x0,x1,x2,x0,255,8); kp[4] = x0; EXPAND_ASSIST(x0,x1,x2,x0,255,16); kp[5] = x0; EXPAND_ASSIST(x0,x1,x2,x0,255,32); kp[6] = x0; EXPAND_ASSIST(x0,x1,x2,x0,255,64); kp[7] = x0; EXPAND_ASSIST(x0,x1,x2,x0,255,128); kp[8] = x0; EXPAND_ASSIST(x0,x1,x2,x0,255,27); kp[9] = x0; EXPAND_ASSIST(x0,x1,x2,x0,255,54); kp[10] = x0; } static void AES_192_Key_Expansion(const unsigned char *userkey, void *key) { __m128i x0,x1,x2,x3,tmp,*kp = (__m128i *)key; kp[0] = x0 = _mm_loadu_si128((__m128i*)userkey); tmp = x3 = _mm_loadu_si128((__m128i*)(userkey+16)); x2 = _mm_setzero_si128(); EXPAND192_STEP(1,1); EXPAND192_STEP(4,4); EXPAND192_STEP(7,16); EXPAND192_STEP(10,64); } static void AES_256_Key_Expansion(const unsigned char *userkey, void *key) { __m128i x0,x1,x2,x3,*kp = (__m128i *)key; kp[0] = x0 = _mm_loadu_si128((__m128i*)userkey ); kp[1] = x3 = _mm_loadu_si128((__m128i*)(userkey+16)); x2 = _mm_setzero_si128(); EXPAND_ASSIST(x0,x1,x2,x3,255,1); kp[2] = x0; EXPAND_ASSIST(x3,x1,x2,x0,170,1); kp[3] = x3; EXPAND_ASSIST(x0,x1,x2,x3,255,2); kp[4] = x0; EXPAND_ASSIST(x3,x1,x2,x0,170,2); kp[5] = x3; EXPAND_ASSIST(x0,x1,x2,x3,255,4); kp[6] = x0; EXPAND_ASSIST(x3,x1,x2,x0,170,4); kp[7] = x3; EXPAND_ASSIST(x0,x1,x2,x3,255,8); kp[8] = x0; EXPAND_ASSIST(x3,x1,x2,x0,170,8); kp[9] = x3; EXPAND_ASSIST(x0,x1,x2,x3,255,16); kp[10] = x0; EXPAND_ASSIST(x3,x1,x2,x0,170,16); kp[11] = x3; EXPAND_ASSIST(x0,x1,x2,x3,255,32); kp[12] = x0; EXPAND_ASSIST(x3,x1,x2,x0,170,32); kp[13] = x3; EXPAND_ASSIST(x0,x1,x2,x3,255,64); kp[14] = x0; } static int AES_set_encrypt_key(const unsigned char *userKey, const int bits, AES_KEY *key) { if (bits == 128) { AES_128_Key_Expansion (userKey,key); } else if (bits == 192) { AES_192_Key_Expansion (userKey,key); } else if (bits == 256) { AES_256_Key_Expansion (userKey,key); } #if (OCB_KEY_LEN == 0) key->rounds = 6+bits/32; #endif return 0; } static void AES_set_decrypt_key_fast(AES_KEY *dkey, const AES_KEY *ekey) { int j = 0; int i = ROUNDS(ekey); #if (OCB_KEY_LEN == 0) dkey->rounds = i; #endif dkey->rd_key[i--] = ekey->rd_key[j++]; while (i) dkey->rd_key[i--] = _mm_aesimc_si128(ekey->rd_key[j++]); dkey->rd_key[i] = ekey->rd_key[j]; } static int AES_set_decrypt_key(const unsigned char *userKey, const int bits, AES_KEY *key) { AES_KEY temp_key; AES_set_encrypt_key(userKey,bits,&temp_key); AES_set_decrypt_key_fast(key, &temp_key); return 0; } static inline void AES_encrypt(const unsigned char *in, unsigned char *out, const AES_KEY *key) { int j,rnds=ROUNDS(key); const __m128i *sched = ((__m128i *)(key->rd_key)); __m128i tmp = _mm_load_si128 ((__m128i*)in); tmp = _mm_xor_si128 (tmp,sched[0]); for (j=1; jrd_key)); __m128i tmp = _mm_load_si128 ((__m128i*)in); tmp = _mm_xor_si128 (tmp,sched[0]); for (j=1; jrd_key)); for (i=0; ird_key)); for (i=0; iL[_tz]) #else static block getL(const ae_ctx *ctx, unsigned tz) { if (tz < L_TABLE_SZ) return ctx->L[tz]; else { unsigned i; /* Bring L[MAX] into registers, make it register correct */ block rval = swap_if_le(ctx->L[L_TABLE_SZ-1]); rval = double_block(rval); for (i=L_TABLE_SZ; i < tz; i++) rval = double_block(rval); return swap_if_le(rval); /* To memory correct */ } } #endif /* ----------------------------------------------------------------------- */ /* Public functions */ /* ----------------------------------------------------------------------- */ /* 32-bit SSE2 and Altivec systems need to be forced to allocate memory on 16-byte alignments. (I believe all major 64-bit systems do already.) */ /* Mosh uses its own AlignedBuffer class, not ae_allocate() or ae_free(). */ /* ----------------------------------------------------------------------- */ int ae_clear (ae_ctx *ctx) /* Zero ae_ctx and undo initialization */ { memset(ctx, 0, sizeof(ae_ctx)); return AE_SUCCESS; } int ae_ctx_sizeof(void) { return (int) sizeof(ae_ctx); } /* ----------------------------------------------------------------------- */ int ae_init(ae_ctx *ctx, const void *key, int key_len, int nonce_len, int tag_len) { unsigned i; block tmp_blk; if (nonce_len != 12) return AE_NOT_SUPPORTED; /* Initialize encryption & decryption keys */ #if (OCB_KEY_LEN > 0) key_len = OCB_KEY_LEN; #endif AES_set_encrypt_key((unsigned char *)key, key_len*8, &ctx->encrypt_key); #if USE_AES_NI AES_set_decrypt_key_fast(&ctx->decrypt_key,&ctx->encrypt_key); #else AES_set_decrypt_key((unsigned char *)key, (int)(key_len*8), &ctx->decrypt_key); #endif /* Zero things that need zeroing */ ctx->cached_Top = ctx->ad_checksum = zero_block(); ctx->ad_blocks_processed = 0; /* Compute key-dependent values */ AES_encrypt((unsigned char *)&ctx->cached_Top, (unsigned char *)&ctx->Lstar, &ctx->encrypt_key); tmp_blk = swap_if_le(ctx->Lstar); tmp_blk = double_block(tmp_blk); ctx->Ldollar = swap_if_le(tmp_blk); tmp_blk = double_block(tmp_blk); ctx->L[0] = swap_if_le(tmp_blk); for (i = 1; i < L_TABLE_SZ; i++) { tmp_blk = double_block(tmp_blk); ctx->L[i] = swap_if_le(tmp_blk); } #if (OCB_TAG_LEN == 0) ctx->tag_len = tag_len; #else (void) tag_len; /* Suppress var not used error */ #endif return AE_SUCCESS; } /* ----------------------------------------------------------------------- */ static block gen_offset_from_nonce(ae_ctx *ctx, const void *nonce) { const union { unsigned x; unsigned char endian; } little = { 1 }; union { uint32_t u32[4]; uint8_t u8[16]; block bl; } tmp; unsigned idx; /* Replace cached nonce Top if needed */ tmp.u32[0] = (little.endian?0x01000000:0x00000001); tmp.u32[1] = ((uint32_t *)nonce)[0]; tmp.u32[2] = ((uint32_t *)nonce)[1]; tmp.u32[3] = ((uint32_t *)nonce)[2]; idx = (unsigned)(tmp.u8[15] & 0x3f); /* Get low 6 bits of nonce */ tmp.u8[15] = tmp.u8[15] & 0xc0; /* Zero low 6 bits of nonce */ if ( unequal_blocks(tmp.bl,ctx->cached_Top) ) { /* Cached? */ ctx->cached_Top = tmp.bl; /* Update cache, KtopStr */ AES_encrypt(tmp.u8, (unsigned char *)&ctx->KtopStr, &ctx->encrypt_key); if (little.endian) { /* Make Register Correct */ ctx->KtopStr[0] = bswap64(ctx->KtopStr[0]); ctx->KtopStr[1] = bswap64(ctx->KtopStr[1]); } ctx->KtopStr[2] = ctx->KtopStr[0] ^ (ctx->KtopStr[0] << 8) ^ (ctx->KtopStr[1] >> 56); } return gen_offset(ctx->KtopStr, idx); } static void process_ad(ae_ctx *ctx, const void *ad, int ad_len, int final) { union { uint32_t u32[4]; uint8_t u8[16]; block bl; } tmp; block ad_offset, ad_checksum; const block * adp = (block *)ad; unsigned i,k,tz,remaining; ad_offset = ctx->ad_offset; ad_checksum = ctx->ad_checksum; i = ad_len/(BPI*16); if (i) { unsigned ad_block_num = ctx->ad_blocks_processed; do { block ta[BPI], oa[BPI]; ad_block_num += BPI; tz = ntz(ad_block_num); oa[0] = xor_block(ad_offset, ctx->L[0]); ta[0] = xor_block(oa[0], adp[0]); oa[1] = xor_block(oa[0], ctx->L[1]); ta[1] = xor_block(oa[1], adp[1]); oa[2] = xor_block(ad_offset, ctx->L[1]); ta[2] = xor_block(oa[2], adp[2]); #if BPI == 4 ad_offset = xor_block(oa[2], getL(ctx, tz)); ta[3] = xor_block(ad_offset, adp[3]); #elif BPI == 8 oa[3] = xor_block(oa[2], ctx->L[2]); ta[3] = xor_block(oa[3], adp[3]); oa[4] = xor_block(oa[1], ctx->L[2]); ta[4] = xor_block(oa[4], adp[4]); oa[5] = xor_block(oa[0], ctx->L[2]); ta[5] = xor_block(oa[5], adp[5]); oa[6] = xor_block(ad_offset, ctx->L[2]); ta[6] = xor_block(oa[6], adp[6]); ad_offset = xor_block(oa[6], getL(ctx, tz)); ta[7] = xor_block(ad_offset, adp[7]); #endif AES_ecb_encrypt_blks(ta,BPI,&ctx->encrypt_key); ad_checksum = xor_block(ad_checksum, ta[0]); ad_checksum = xor_block(ad_checksum, ta[1]); ad_checksum = xor_block(ad_checksum, ta[2]); ad_checksum = xor_block(ad_checksum, ta[3]); #if (BPI == 8) ad_checksum = xor_block(ad_checksum, ta[4]); ad_checksum = xor_block(ad_checksum, ta[5]); ad_checksum = xor_block(ad_checksum, ta[6]); ad_checksum = xor_block(ad_checksum, ta[7]); #endif adp += BPI; } while (--i); ctx->ad_blocks_processed = ad_block_num; ctx->ad_offset = ad_offset; ctx->ad_checksum = ad_checksum; } if (final) { block ta[BPI]; /* Process remaining associated data, compute its tag contribution */ remaining = ((unsigned)ad_len) % (BPI*16); if (remaining) { k=0; #if (BPI == 8) if (remaining >= 64) { tmp.bl = xor_block(ad_offset, ctx->L[0]); ta[0] = xor_block(tmp.bl, adp[0]); tmp.bl = xor_block(tmp.bl, ctx->L[1]); ta[1] = xor_block(tmp.bl, adp[1]); ad_offset = xor_block(ad_offset, ctx->L[1]); ta[2] = xor_block(ad_offset, adp[2]); ad_offset = xor_block(ad_offset, ctx->L[2]); ta[3] = xor_block(ad_offset, adp[3]); remaining -= 64; k=4; } #endif if (remaining >= 32) { ad_offset = xor_block(ad_offset, ctx->L[0]); ta[k] = xor_block(ad_offset, adp[k]); ad_offset = xor_block(ad_offset, getL(ctx, ntz(k+2))); ta[k+1] = xor_block(ad_offset, adp[k+1]); remaining -= 32; k+=2; } if (remaining >= 16) { ad_offset = xor_block(ad_offset, ctx->L[0]); ta[k] = xor_block(ad_offset, adp[k]); remaining = remaining - 16; ++k; } if (remaining) { ad_offset = xor_block(ad_offset,ctx->Lstar); tmp.bl = zero_block(); memcpy(tmp.u8, adp+k, remaining); tmp.u8[remaining] = (unsigned char)0x80u; ta[k] = xor_block(ad_offset, tmp.bl); ++k; } AES_ecb_encrypt_blks(ta,k,&ctx->encrypt_key); switch (k) { #if (BPI == 8) case 8: ad_checksum = xor_block(ad_checksum, ta[7]); case 7: ad_checksum = xor_block(ad_checksum, ta[6]); case 6: ad_checksum = xor_block(ad_checksum, ta[5]); case 5: ad_checksum = xor_block(ad_checksum, ta[4]); #endif case 4: ad_checksum = xor_block(ad_checksum, ta[3]); case 3: ad_checksum = xor_block(ad_checksum, ta[2]); case 2: ad_checksum = xor_block(ad_checksum, ta[1]); case 1: ad_checksum = xor_block(ad_checksum, ta[0]); } ctx->ad_checksum = ad_checksum; } } } /* ----------------------------------------------------------------------- */ int ae_encrypt(ae_ctx * ctx, const void * nonce, const void *pt, int pt_len, const void *ad, int ad_len, void *ct, void *tag, int final) { union { uint32_t u32[4]; uint8_t u8[16]; block bl; } tmp; block offset, checksum; unsigned i, k; block * ctp = (block *)ct; const block * ptp = (block *)pt; /* Non-null nonce means start of new message, init per-message values */ if (nonce) { ctx->offset = gen_offset_from_nonce(ctx, nonce); ctx->ad_offset = ctx->checksum = zero_block(); ctx->ad_blocks_processed = ctx->blocks_processed = 0; if (ad_len >= 0) ctx->ad_checksum = zero_block(); } /* Process associated data */ if (ad_len > 0) process_ad(ctx, ad, ad_len, final); /* Encrypt plaintext data BPI blocks at a time */ offset = ctx->offset; checksum = ctx->checksum; i = pt_len/(BPI*16); if (i) { block oa[BPI]; unsigned block_num = ctx->blocks_processed; oa[BPI-1] = offset; do { block ta[BPI]; block_num += BPI; oa[0] = xor_block(oa[BPI-1], ctx->L[0]); ta[0] = xor_block(oa[0], ptp[0]); checksum = xor_block(checksum, ptp[0]); oa[1] = xor_block(oa[0], ctx->L[1]); ta[1] = xor_block(oa[1], ptp[1]); checksum = xor_block(checksum, ptp[1]); oa[2] = xor_block(oa[1], ctx->L[0]); ta[2] = xor_block(oa[2], ptp[2]); checksum = xor_block(checksum, ptp[2]); #if BPI == 4 oa[3] = xor_block(oa[2], getL(ctx, ntz(block_num))); ta[3] = xor_block(oa[3], ptp[3]); checksum = xor_block(checksum, ptp[3]); #elif BPI == 8 oa[3] = xor_block(oa[2], ctx->L[2]); ta[3] = xor_block(oa[3], ptp[3]); checksum = xor_block(checksum, ptp[3]); oa[4] = xor_block(oa[1], ctx->L[2]); ta[4] = xor_block(oa[4], ptp[4]); checksum = xor_block(checksum, ptp[4]); oa[5] = xor_block(oa[0], ctx->L[2]); ta[5] = xor_block(oa[5], ptp[5]); checksum = xor_block(checksum, ptp[5]); oa[6] = xor_block(oa[7], ctx->L[2]); ta[6] = xor_block(oa[6], ptp[6]); checksum = xor_block(checksum, ptp[6]); oa[7] = xor_block(oa[6], getL(ctx, ntz(block_num))); ta[7] = xor_block(oa[7], ptp[7]); checksum = xor_block(checksum, ptp[7]); #endif AES_ecb_encrypt_blks(ta,BPI,&ctx->encrypt_key); ctp[0] = xor_block(ta[0], oa[0]); ctp[1] = xor_block(ta[1], oa[1]); ctp[2] = xor_block(ta[2], oa[2]); ctp[3] = xor_block(ta[3], oa[3]); #if (BPI == 8) ctp[4] = xor_block(ta[4], oa[4]); ctp[5] = xor_block(ta[5], oa[5]); ctp[6] = xor_block(ta[6], oa[6]); ctp[7] = xor_block(ta[7], oa[7]); #endif ptp += BPI; ctp += BPI; } while (--i); ctx->offset = offset = oa[BPI-1]; ctx->blocks_processed = block_num; ctx->checksum = checksum; } if (final) { block ta[BPI+1], oa[BPI]; /* Process remaining plaintext and compute its tag contribution */ unsigned remaining = ((unsigned)pt_len) % (BPI*16); k = 0; /* How many blocks in ta[] need ECBing */ if (remaining) { #if (BPI == 8) if (remaining >= 64) { oa[0] = xor_block(offset, ctx->L[0]); ta[0] = xor_block(oa[0], ptp[0]); checksum = xor_block(checksum, ptp[0]); oa[1] = xor_block(oa[0], ctx->L[1]); ta[1] = xor_block(oa[1], ptp[1]); checksum = xor_block(checksum, ptp[1]); oa[2] = xor_block(oa[1], ctx->L[0]); ta[2] = xor_block(oa[2], ptp[2]); checksum = xor_block(checksum, ptp[2]); offset = oa[3] = xor_block(oa[2], ctx->L[2]); ta[3] = xor_block(offset, ptp[3]); checksum = xor_block(checksum, ptp[3]); remaining -= 64; k = 4; } #endif if (remaining >= 32) { oa[k] = xor_block(offset, ctx->L[0]); ta[k] = xor_block(oa[k], ptp[k]); checksum = xor_block(checksum, ptp[k]); offset = oa[k+1] = xor_block(oa[k], ctx->L[1]); ta[k+1] = xor_block(offset, ptp[k+1]); checksum = xor_block(checksum, ptp[k+1]); remaining -= 32; k+=2; } if (remaining >= 16) { offset = oa[k] = xor_block(offset, ctx->L[0]); ta[k] = xor_block(offset, ptp[k]); checksum = xor_block(checksum, ptp[k]); remaining -= 16; ++k; } if (remaining) { tmp.bl = zero_block(); memcpy(tmp.u8, ptp+k, remaining); tmp.u8[remaining] = (unsigned char)0x80u; checksum = xor_block(checksum, tmp.bl); ta[k] = offset = xor_block(offset,ctx->Lstar); ++k; } } offset = xor_block(offset, ctx->Ldollar); /* Part of tag gen */ ta[k] = xor_block(offset, checksum); /* Part of tag gen */ AES_ecb_encrypt_blks(ta,k+1,&ctx->encrypt_key); offset = xor_block(ta[k], ctx->ad_checksum); /* Part of tag gen */ if (remaining) { --k; tmp.bl = xor_block(tmp.bl, ta[k]); memcpy(ctp+k, tmp.u8, remaining); } switch (k) { #if (BPI == 8) case 7: ctp[6] = xor_block(ta[6], oa[6]); case 6: ctp[5] = xor_block(ta[5], oa[5]); case 5: ctp[4] = xor_block(ta[4], oa[4]); case 4: ctp[3] = xor_block(ta[3], oa[3]); #endif case 3: ctp[2] = xor_block(ta[2], oa[2]); case 2: ctp[1] = xor_block(ta[1], oa[1]); case 1: ctp[0] = xor_block(ta[0], oa[0]); } /* Tag is placed at the correct location */ if (tag) { #if (OCB_TAG_LEN == 16) *(block *)tag = offset; #elif (OCB_TAG_LEN > 0) memcpy((char *)tag, &offset, OCB_TAG_LEN); #else memcpy((char *)tag, &offset, ctx->tag_len); #endif } else { #if (OCB_TAG_LEN > 0) memcpy((char *)ct + pt_len, &offset, OCB_TAG_LEN); pt_len += OCB_TAG_LEN; #else memcpy((char *)ct + pt_len, &offset, ctx->tag_len); pt_len += ctx->tag_len; #endif } } return (int) pt_len; } /* ----------------------------------------------------------------------- */ /* Compare two regions of memory, taking a constant amount of time for a given buffer size -- under certain assumptions about the compiler and machine, of course. Use this to avoid timing side-channel attacks. Returns 0 for memory regions with equal contents; non-zero otherwise. */ static int constant_time_memcmp(const void *av, const void *bv, size_t n) { const uint8_t *a = (const uint8_t *) av; const uint8_t *b = (const uint8_t *) bv; uint8_t result = 0; size_t i; for (i=0; i 0) ct_len -= OCB_TAG_LEN; #else ct_len -= ctx->tag_len; #endif /* Non-null nonce means start of new message, init per-message values */ if (nonce) { ctx->offset = gen_offset_from_nonce(ctx, nonce); ctx->ad_offset = ctx->checksum = zero_block(); ctx->ad_blocks_processed = ctx->blocks_processed = 0; if (ad_len >= 0) ctx->ad_checksum = zero_block(); } /* Process associated data */ if (ad_len > 0) process_ad(ctx, ad, ad_len, final); /* Encrypt plaintext data BPI blocks at a time */ offset = ctx->offset; checksum = ctx->checksum; i = ct_len/(BPI*16); if (i) { block oa[BPI]; unsigned block_num = ctx->blocks_processed; oa[BPI-1] = offset; do { block ta[BPI]; block_num += BPI; oa[0] = xor_block(oa[BPI-1], ctx->L[0]); ta[0] = xor_block(oa[0], ctp[0]); oa[1] = xor_block(oa[0], ctx->L[1]); ta[1] = xor_block(oa[1], ctp[1]); oa[2] = xor_block(oa[1], ctx->L[0]); ta[2] = xor_block(oa[2], ctp[2]); #if BPI == 4 oa[3] = xor_block(oa[2], getL(ctx, ntz(block_num))); ta[3] = xor_block(oa[3], ctp[3]); #elif BPI == 8 oa[3] = xor_block(oa[2], ctx->L[2]); ta[3] = xor_block(oa[3], ctp[3]); oa[4] = xor_block(oa[1], ctx->L[2]); ta[4] = xor_block(oa[4], ctp[4]); oa[5] = xor_block(oa[0], ctx->L[2]); ta[5] = xor_block(oa[5], ctp[5]); oa[6] = xor_block(oa[7], ctx->L[2]); ta[6] = xor_block(oa[6], ctp[6]); oa[7] = xor_block(oa[6], getL(ctx, ntz(block_num))); ta[7] = xor_block(oa[7], ctp[7]); #endif AES_ecb_decrypt_blks(ta,BPI,&ctx->decrypt_key); ptp[0] = xor_block(ta[0], oa[0]); checksum = xor_block(checksum, ptp[0]); ptp[1] = xor_block(ta[1], oa[1]); checksum = xor_block(checksum, ptp[1]); ptp[2] = xor_block(ta[2], oa[2]); checksum = xor_block(checksum, ptp[2]); ptp[3] = xor_block(ta[3], oa[3]); checksum = xor_block(checksum, ptp[3]); #if (BPI == 8) ptp[4] = xor_block(ta[4], oa[4]); checksum = xor_block(checksum, ptp[4]); ptp[5] = xor_block(ta[5], oa[5]); checksum = xor_block(checksum, ptp[5]); ptp[6] = xor_block(ta[6], oa[6]); checksum = xor_block(checksum, ptp[6]); ptp[7] = xor_block(ta[7], oa[7]); checksum = xor_block(checksum, ptp[7]); #endif ptp += BPI; ctp += BPI; } while (--i); ctx->offset = offset = oa[BPI-1]; ctx->blocks_processed = block_num; ctx->checksum = checksum; } if (final) { block ta[BPI+1], oa[BPI]; /* Process remaining plaintext and compute its tag contribution */ unsigned remaining = ((unsigned)ct_len) % (BPI*16); k = 0; /* How many blocks in ta[] need ECBing */ if (remaining) { #if (BPI == 8) if (remaining >= 64) { oa[0] = xor_block(offset, ctx->L[0]); ta[0] = xor_block(oa[0], ctp[0]); oa[1] = xor_block(oa[0], ctx->L[1]); ta[1] = xor_block(oa[1], ctp[1]); oa[2] = xor_block(oa[1], ctx->L[0]); ta[2] = xor_block(oa[2], ctp[2]); offset = oa[3] = xor_block(oa[2], ctx->L[2]); ta[3] = xor_block(offset, ctp[3]); remaining -= 64; k = 4; } #endif if (remaining >= 32) { oa[k] = xor_block(offset, ctx->L[0]); ta[k] = xor_block(oa[k], ctp[k]); offset = oa[k+1] = xor_block(oa[k], ctx->L[1]); ta[k+1] = xor_block(offset, ctp[k+1]); remaining -= 32; k+=2; } if (remaining >= 16) { offset = oa[k] = xor_block(offset, ctx->L[0]); ta[k] = xor_block(offset, ctp[k]); remaining -= 16; ++k; } if (remaining) { block pad; offset = xor_block(offset,ctx->Lstar); AES_encrypt((unsigned char *)&offset, tmp.u8, &ctx->encrypt_key); pad = tmp.bl; memcpy(tmp.u8,ctp+k,remaining); tmp.bl = xor_block(tmp.bl, pad); tmp.u8[remaining] = (unsigned char)0x80u; memcpy(ptp+k, tmp.u8, remaining); checksum = xor_block(checksum, tmp.bl); } } AES_ecb_decrypt_blks(ta,k,&ctx->decrypt_key); switch (k) { #if (BPI == 8) case 7: ptp[6] = xor_block(ta[6], oa[6]); checksum = xor_block(checksum, ptp[6]); case 6: ptp[5] = xor_block(ta[5], oa[5]); checksum = xor_block(checksum, ptp[5]); case 5: ptp[4] = xor_block(ta[4], oa[4]); checksum = xor_block(checksum, ptp[4]); case 4: ptp[3] = xor_block(ta[3], oa[3]); checksum = xor_block(checksum, ptp[3]); #endif case 3: ptp[2] = xor_block(ta[2], oa[2]); checksum = xor_block(checksum, ptp[2]); case 2: ptp[1] = xor_block(ta[1], oa[1]); checksum = xor_block(checksum, ptp[1]); case 1: ptp[0] = xor_block(ta[0], oa[0]); checksum = xor_block(checksum, ptp[0]); } /* Calculate expected tag */ offset = xor_block(offset, ctx->Ldollar); tmp.bl = xor_block(offset, checksum); AES_encrypt(tmp.u8, tmp.u8, &ctx->encrypt_key); tmp.bl = xor_block(tmp.bl, ctx->ad_checksum); /* Full tag */ /* Compare with proposed tag, change ct_len if invalid */ if ((OCB_TAG_LEN == 16) && tag) { if (unequal_blocks(tmp.bl, *(block *)tag)) ct_len = AE_INVALID; } else { #if (OCB_TAG_LEN > 0) int len = OCB_TAG_LEN; #else int len = ctx->tag_len; #endif if (tag) { if (constant_time_memcmp(tag,tmp.u8,len) != 0) ct_len = AE_INVALID; } else { if (constant_time_memcmp((char *)ct + ct_len,tmp.u8,len) != 0) ct_len = AE_INVALID; } } } return ct_len; } /* ----------------------------------------------------------------------- */ /* Simple test program */ /* ----------------------------------------------------------------------- */ #if defined(OCB_TEST_PROGRAM) #include #include #if __GNUC__ #define ALIGN(n) __attribute__ ((aligned(n))) #elif _MSC_VER #define ALIGN(n) __declspec(align(n)) #else /* Not GNU/Microsoft: delete alignment uses. */ #define ALIGN(n) #endif static void pbuf(void *p, unsigned len, const void *s) { unsigned i; if (s) printf("%s", (char *)s); for (i = 0; i < len; i++) printf("%02X", (unsigned)(((unsigned char *)p)[i])); printf("\n"); } static void vectors(ae_ctx *ctx, int len) { ALIGN(16) uint8_t pt[128]; ALIGN(16) uint8_t ct[144]; ALIGN(16) uint8_t nonce[] = {0,1,2,3,4,5,6,7,8,9,10,11}; int i; for (i=0; i < 128; i++) pt[i] = i; i = ae_encrypt(ctx,nonce,pt,len,pt,len,ct,NULL,AE_FINALIZE); printf("P=%d,A=%d: ",len,len); pbuf(ct, i, NULL); i = ae_encrypt(ctx,nonce,pt,0,pt,len,ct,NULL,AE_FINALIZE); printf("P=%d,A=%d: ",0,len); pbuf(ct, i, NULL); i = ae_encrypt(ctx,nonce,pt,len,pt,0,ct,NULL,AE_FINALIZE); printf("P=%d,A=%d: ",len,0); pbuf(ct, i, NULL); } static void validate() { ALIGN(16) uint8_t pt[1024]; ALIGN(16) uint8_t ct[1024]; ALIGN(16) uint8_t tag[16]; ALIGN(16) uint8_t nonce[12] = {0,}; ALIGN(16) uint8_t key[32] = {0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15}; ALIGN(16) uint8_t valid[] = {0xB2,0xB4,0x1C,0xBF,0x9B,0x05,0x03,0x7D, 0xA7,0xF1,0x6C,0x24,0xA3,0x5C,0x1C,0x94}; ae_ctx ctx; uint8_t *val_buf, *next; int i, len; val_buf = (uint8_t *)malloc(22400 + 16); next = val_buf = (uint8_t *)(((size_t)val_buf + 16) & ~((size_t)15)); if (0) { ae_init(&ctx, key, 16, 12, 16); /* pbuf(&ctx, sizeof(ctx), "CTX: "); */ vectors(&ctx,0); vectors(&ctx,8); vectors(&ctx,16); vectors(&ctx,24); vectors(&ctx,32); vectors(&ctx,40); } memset(key,0,32); memset(pt,0,128); ae_init(&ctx, key, 16, 12, 16); /* RFC Vector test */ for (i = 0; i < 128; i++) { int first = ((i/3)/(BPI*16))*(BPI*16); int second = first; int third = i - (first + second); nonce[11] = i; if (0) { ae_encrypt(&ctx,nonce,pt,i,pt,i,ct,NULL,AE_FINALIZE); memcpy(next,ct,(size_t)i+16); next = next+i+16; ae_encrypt(&ctx,nonce,pt,i,pt,0,ct,NULL,AE_FINALIZE); memcpy(next,ct,(size_t)i+16); next = next+i+16; ae_encrypt(&ctx,nonce,pt,0,pt,i,ct,NULL,AE_FINALIZE); memcpy(next,ct,16); next = next+16; } else { ae_encrypt(&ctx,nonce,pt,first,pt,first,ct,NULL,AE_PENDING); ae_encrypt(&ctx,NULL,pt+first,second,pt+first,second,ct+first,NULL,AE_PENDING); ae_encrypt(&ctx,NULL,pt+first+second,third,pt+first+second,third,ct+first+second,NULL,AE_FINALIZE); memcpy(next,ct,(size_t)i+16); next = next+i+16; ae_encrypt(&ctx,nonce,pt,first,pt,0,ct,NULL,AE_PENDING); ae_encrypt(&ctx,NULL,pt+first,second,pt,0,ct+first,NULL,AE_PENDING); ae_encrypt(&ctx,NULL,pt+first+second,third,pt,0,ct+first+second,NULL,AE_FINALIZE); memcpy(next,ct,(size_t)i+16); next = next+i+16; ae_encrypt(&ctx,nonce,pt,0,pt,first,ct,NULL,AE_PENDING); ae_encrypt(&ctx,NULL,pt,0,pt+first,second,ct,NULL,AE_PENDING); ae_encrypt(&ctx,NULL,pt,0,pt+first+second,third,ct,NULL,AE_FINALIZE); memcpy(next,ct,16); next = next+16; } } nonce[11] = 0; ae_encrypt(&ctx,nonce,NULL,0,val_buf,next-val_buf,ct,tag,AE_FINALIZE); pbuf(tag,16,0); if (memcmp(valid,tag,16) == 0) printf("Vectors: PASS\n"); else printf("Vectors: FAIL\n"); /* Encrypt/Decrypt test */ for (i = 0; i < 128; i++) { int first = ((i/3)/(BPI*16))*(BPI*16); int second = first; int third = i - (first + second); nonce[11] = i%128; if (1) { len = ae_encrypt(&ctx,nonce,val_buf,i,val_buf,i,ct,tag,AE_FINALIZE); len = ae_encrypt(&ctx,nonce,val_buf,i,val_buf,-1,ct,tag,AE_FINALIZE); len = ae_decrypt(&ctx,nonce,ct,len,val_buf,-1,pt,tag,AE_FINALIZE); if (len == -1) { printf("Authentication error: %d\n", i); return; } if (len != i) { printf("Length error: %d\n", i); return; } if (memcmp(val_buf,pt,i)) { printf("Decrypt error: %d\n", i); return; } } else { len = ae_encrypt(&ctx,nonce,val_buf,i,val_buf,i,ct,NULL,AE_FINALIZE); ae_decrypt(&ctx,nonce,ct,first,val_buf,first,pt,NULL,AE_PENDING); ae_decrypt(&ctx,NULL,ct+first,second,val_buf+first,second,pt+first,NULL,AE_PENDING); len = ae_decrypt(&ctx,NULL,ct+first+second,len-(first+second),val_buf+first+second,third,pt+first+second,NULL,AE_FINALIZE); if (len == -1) { printf("Authentication error: %d\n", i); return; } if (memcmp(val_buf,pt,i)) { printf("Decrypt error: %d\n", i); return; } } } printf("Decrypt: PASS\n"); } int main() { validate(); return 0; } #endif #if USE_AES_NI char infoString[] = "OCB3 (AES-NI)"; #elif USE_REFERENCE_AES char infoString[] = "OCB3 (Reference)"; #elif USE_OPENSSL_AES char infoString[] = "OCB3 (OpenSSL)"; #endif mosh-1.2.5/src/crypto/prng.h000066400000000000000000000046441255051512600157250ustar00rootroot00000000000000/* Mosh: the mobile shell Copyright 2012 Keith Winstein This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . In addition, as a special exception, the copyright holders give permission to link the code of portions of this program with the OpenSSL library under certain conditions as described in each individual source file, and distribute linked combinations including the two. You must obey the GNU General Public License in all respects for all of the code used other than OpenSSL. If you modify file(s) with this exception, you may extend this exception to your version of the file(s), but you are not obligated to do so. If you do not wish to do so, delete this exception statement from your version. If you delete this exception statement from all source files in the program, then also delete it here. */ #ifndef PRNG_HPP #define PRNG_HPP #include #include #include #include "crypto.h" /* Read random bytes from /dev/urandom. We rely on stdio buffering for efficiency. */ static const char rdev[] = "/dev/urandom"; using namespace Crypto; class PRNG { private: std::ifstream randfile; /* unimplemented to satisfy -Weffc++ */ PRNG( const PRNG & ); PRNG & operator=( const PRNG & ); public: PRNG() : randfile( rdev, std::ifstream::in | std::ifstream::binary ) {} void fill( void *dest, size_t size ) { if ( 0 == size ) { return; } randfile.read( static_cast( dest ), size ); if ( !randfile ) { throw CryptoException( "Could not read from " + std::string( rdev ) ); } } uint8_t uint8() { uint8_t x; fill( &x, 1 ); return x; } uint32_t uint32() { uint32_t x; fill( &x, 4 ); return x; } uint64_t uint64() { uint64_t x; fill( &x, 8 ); return x; } }; #endif mosh-1.2.5/src/examples/000077500000000000000000000000001255051512600150745ustar00rootroot00000000000000mosh-1.2.5/src/examples/.gitignore000066400000000000000000000000661255051512600170660ustar00rootroot00000000000000/decrypt /encrypt /ntester /parse /termemu /benchmark mosh-1.2.5/src/examples/Makefile.am000066400000000000000000000035401255051512600171320ustar00rootroot00000000000000AM_CXXFLAGS = $(WARNING_CXXFLAGS) $(PICKY_CXXFLAGS) $(HARDEN_CFLAGS) $(MISC_CXXFLAGS) AM_LDFLAGS = $(HARDEN_LDFLAGS) if BUILD_EXAMPLES noinst_PROGRAMS = encrypt decrypt ntester parse termemu benchmark endif encrypt_SOURCES = encrypt.cc encrypt_CPPFLAGS = -I$(srcdir)/../crypto encrypt_LDADD = ../crypto/libmoshcrypto.a $(OPENSSL_LIBS) decrypt_SOURCES = decrypt.cc decrypt_CPPFLAGS = -I$(srcdir)/../crypto decrypt_LDADD = ../crypto/libmoshcrypto.a $(OPENSSL_LIBS) parse_SOURCES = parse.cc parse_CPPFLAGS = -I$(srcdir)/../terminal -I$(srcdir)/../util parse_LDADD = ../terminal/libmoshterminal.a ../util/libmoshutil.a $(LIBUTIL) termemu_SOURCES = termemu.cc termemu_CPPFLAGS = -I$(srcdir)/../terminal -I$(srcdir)/../util -I$(srcdir)/../statesync -I../protobufs termemu_LDADD = ../terminal/libmoshterminal.a ../util/libmoshutil.a ../statesync/libmoshstatesync.a ../protobufs/libmoshprotos.a $(LIBUTIL) $(TINFO_LIBS) $(protobuf_LIBS) ntester_SOURCES = ntester.cc ntester_CPPFLAGS = -I$(srcdir)/../util -I$(srcdir)/../statesync -I$(srcdir)/../terminal -I$(srcdir)/../network -I$(srcdir)/../crypto -I../protobufs $(protobuf_CFLAGS) ntester_LDADD = ../statesync/libmoshstatesync.a ../terminal/libmoshterminal.a ../network/libmoshnetwork.a ../crypto/libmoshcrypto.a ../protobufs/libmoshprotos.a ../util/libmoshutil.a $(LIBUTIL) -lm $(protobuf_LIBS) $(OPENSSL_LIBS) benchmark_SOURCES = benchmark.cc benchmark_CPPFLAGS = -I$(srcdir)/../util -I$(srcdir)/../statesync -I$(srcdir)/../terminal -I../protobufs -I$(srcdir)/../frontend -I$(srcdir)/../crypto -I$(srcdir)/../network $(protobuf_CFLAGS) benchmark_LDADD = ../frontend/terminaloverlay.o ../statesync/libmoshstatesync.a ../terminal/libmoshterminal.a ../protobufs/libmoshprotos.a ../network/libmoshnetwork.a ../crypto/libmoshcrypto.a ../util/libmoshutil.a $(STDDJB_LDFLAGS) $(LIBUTIL) -lm $(TINFO_LIBS) $(protobuf_LIBS) $(OPENSSL_LIBS) mosh-1.2.5/src/examples/benchmark.cc000066400000000000000000000073641255051512600173470ustar00rootroot00000000000000/* Mosh: the mobile shell Copyright 2012 Keith Winstein This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . In addition, as a special exception, the copyright holders give permission to link the code of portions of this program with the OpenSSL library under certain conditions as described in each individual source file, and distribute linked combinations including the two. You must obey the GNU General Public License in all respects for all of the code used other than OpenSSL. If you modify file(s) with this exception, you may extend this exception to your version of the file(s), but you are not obligated to do so. If you do not wish to do so, delete this exception statement from your version. If you delete this exception statement from all source files in the program, then also delete it here. */ #include "config.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #if HAVE_PTY_H #include #elif HAVE_UTIL_H #include #endif #include "swrite.h" #include "completeterminal.h" #include "user.h" #include "terminaloverlay.h" #include "locale_utils.h" #include "fatal_assert.h" const int ITERATIONS = 100000; using namespace Terminal; int main( int argc, char **argv ) { try { int fbmod = 0; int width = 80, height = 24; int iterations = ITERATIONS; if (argc > 1) { iterations = atoi(argv[1]); if (iterations < 1 || iterations > 1000000000) { fprintf(stderr, "bogus iteration count\n"); exit(1); } } if (argc > 3) { width = atoi(argv[2]); height = atoi(argv[3]); if (width < 1 || width > 1000 || height < 1 || height > 1000) { fprintf(stderr, "bogus window size\n"); exit(1); } } Framebuffer local_framebuffers[ 2 ] = { Framebuffer(width,height), Framebuffer(width,height) }; Framebuffer *local_framebuffer = &(local_framebuffers[ fbmod ]); Framebuffer *new_state = &(local_framebuffers[ !fbmod ]); Overlay::OverlayManager overlays; Display display( true ); Complete local_terminal( width, height ); /* Adopt native locale */ set_native_locale(); fatal_assert( is_utf8_locale() ); for ( int i = 0; i < iterations; i++ ) { /* type a character */ overlays.get_prediction_engine().new_user_byte( i + 'x', *local_framebuffer ); /* fetch target state */ *new_state = local_terminal.get_fb(); /* apply local overlays */ overlays.apply( *new_state ); /* calculate minimal difference from where we are */ const string diff( display.new_frame( false, *local_framebuffer, *new_state ) ); /* make sure to use diff */ if ( diff.size() > INT_MAX ) { exit( 1 ); } fbmod = !fbmod; local_framebuffer = &(local_framebuffers[ fbmod ]); new_state = &(local_framebuffers[ !fbmod ]); } } catch ( const std::exception &e ) { fprintf( stderr, "Exception caught: %s\n", e.what() ); return 1; } return 0; } mosh-1.2.5/src/examples/decrypt.cc000066400000000000000000000041521255051512600170570ustar00rootroot00000000000000/* Mosh: the mobile shell Copyright 2012 Keith Winstein This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . In addition, as a special exception, the copyright holders give permission to link the code of portions of this program with the OpenSSL library under certain conditions as described in each individual source file, and distribute linked combinations including the two. You must obey the GNU General Public License in all respects for all of the code used other than OpenSSL. If you modify file(s) with this exception, you may extend this exception to your version of the file(s), but you are not obligated to do so. If you do not wish to do so, delete this exception statement from your version. If you delete this exception statement from all source files in the program, then also delete it here. */ #include #include #include #include "crypto.h" using namespace Crypto; using namespace std; int main( int argc, char *argv[] ) { if ( argc != 2 ) { fprintf( stderr, "Usage: %s KEY\n", argv[ 0 ] ); return 1; } try { Base64Key key( argv[ 1 ] ); Session session( key ); /* Read input */ ostringstream input; input << cin.rdbuf(); /* Decrypt message */ Message message = session.decrypt( input.str() ); fprintf( stderr, "Nonce = %ld\n", (long)message.nonce.val() ); cout << message.text; } catch ( const CryptoException &e ) { cerr << e.what() << endl; exit( 1 ); } return 0; } mosh-1.2.5/src/examples/encrypt.cc000066400000000000000000000042051255051512600170700ustar00rootroot00000000000000/* Mosh: the mobile shell Copyright 2012 Keith Winstein This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . In addition, as a special exception, the copyright holders give permission to link the code of portions of this program with the OpenSSL library under certain conditions as described in each individual source file, and distribute linked combinations including the two. You must obey the GNU General Public License in all respects for all of the code used other than OpenSSL. If you modify file(s) with this exception, you may extend this exception to your version of the file(s), but you are not obligated to do so. If you do not wish to do so, delete this exception statement from your version. If you delete this exception statement from all source files in the program, then also delete it here. */ #include #include #include #include "crypto.h" using namespace Crypto; using namespace std; int main( int argc, char *argv[] ) { if ( argc != 2 ) { fprintf( stderr, "Usage: %s NONCE\n", argv[ 0 ] ); return 1; } try { Base64Key key; Session session( key ); Nonce nonce( myatoi( argv[ 1 ] ) ); /* Read input */ ostringstream input; input << cin.rdbuf(); /* Encrypt message */ string ciphertext = session.encrypt( Message( nonce, input.str() ) ); cerr << "Key: " << key.printable_key() << endl; cout << ciphertext; } catch ( const CryptoException &e ) { cerr << e.what() << endl; exit( 1 ); } return 0; } mosh-1.2.5/src/examples/ntester.cc000066400000000000000000000112231255051512600170660ustar00rootroot00000000000000/* Mosh: the mobile shell Copyright 2012 Keith Winstein This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . In addition, as a special exception, the copyright holders give permission to link the code of portions of this program with the OpenSSL library under certain conditions as described in each individual source file, and distribute linked combinations including the two. You must obey the GNU General Public License in all respects for all of the code used other than OpenSSL. If you modify file(s) with this exception, you may extend this exception to your version of the file(s), but you are not obligated to do so. If you do not wish to do so, delete this exception statement from your version. If you delete this exception statement from all source files in the program, then also delete it here. */ #include #include #include "user.h" #include "fatal_assert.h" #include "pty_compat.h" #include "networktransport.cc" #include "select.h" using namespace Network; int main( int argc, char *argv[] ) { bool server = true; char *key; char *ip; char *port; UserStream me, remote; Transport *n; try { if ( argc > 1 ) { server = false; /* client */ key = argv[ 1 ]; ip = argv[ 2 ]; port = argv[ 3 ]; n = new Transport( me, remote, key, ip, port ); } else { n = new Transport( me, remote, NULL, NULL ); } fprintf( stderr, "Port bound is %s, key is %s\n", n->port().c_str(), n->get_key().c_str() ); } catch ( const std::exception &e ) { fprintf( stderr, "Fatal startup error: %s\n", e.what() ); exit( 1 ); } if ( server ) { Select &sel = Select::get_instance(); uint64_t last_num = n->get_remote_state_num(); while ( true ) { try { sel.clear_fds(); std::vector< int > fd_list( n->fds() ); assert( fd_list.size() == 1 ); /* servers don't hop */ int network_fd = fd_list.back(); sel.add_fd( network_fd ); if ( sel.select( n->wait_time() ) < 0 ) { perror( "select" ); exit( 1 ); } n->tick(); if ( sel.read( network_fd ) ) { n->recv(); if ( n->get_remote_state_num() != last_num ) { fprintf( stderr, "[%d=>%d %s]", (int)last_num, (int)n->get_remote_state_num(), n->get_remote_diff().c_str() ); last_num = n->get_remote_state_num(); } } } catch ( const std::exception &e ) { fprintf( stderr, "Server error: %s\n", e.what() ); } } } else { struct termios saved_termios; struct termios the_termios; if ( tcgetattr( STDIN_FILENO, &the_termios ) < 0 ) { perror( "tcgetattr" ); exit( 1 ); } saved_termios = the_termios; cfmakeraw( &the_termios ); if ( tcsetattr( STDIN_FILENO, TCSANOW, &the_termios ) < 0 ) { perror( "tcsetattr" ); exit( 1 ); } Select &sel = Select::get_instance(); while( true ) { sel.clear_fds(); sel.add_fd( STDIN_FILENO ); std::vector< int > fd_list( n->fds() ); for ( std::vector< int >::const_iterator it = fd_list.begin(); it != fd_list.end(); it++ ) { sel.add_fd( *it ); } try { if ( sel.select( n->wait_time() ) < 0 ) { perror( "select" ); } n->tick(); if ( sel.read( STDIN_FILENO ) ) { char x; fatal_assert( read( STDIN_FILENO, &x, 1 ) == 1 ); n->get_current_state().push_back( Parser::UserByte( x ) ); } bool network_ready_to_read = false; for ( std::vector< int >::const_iterator it = fd_list.begin(); it != fd_list.end(); it++ ) { if ( sel.read( *it ) ) { /* packet received from the network */ /* we only read one socket each run */ network_ready_to_read = true; } if ( sel.error( *it ) ) { break; } } if ( network_ready_to_read ) { n->recv(); } } catch ( const std::exception &e ) { fprintf( stderr, "Client error: %s\n", e.what() ); } } if ( tcsetattr( STDIN_FILENO, TCSANOW, &saved_termios ) < 0 ) { perror( "tcsetattr" ); exit( 1 ); } } delete n; } mosh-1.2.5/src/examples/parse.cc000066400000000000000000000130251255051512600165160ustar00rootroot00000000000000/* Mosh: the mobile shell Copyright 2012 Keith Winstein This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . In addition, as a special exception, the copyright holders give permission to link the code of portions of this program with the OpenSSL library under certain conditions as described in each individual source file, and distribute linked combinations including the two. You must obey the GNU General Public License in all respects for all of the code used other than OpenSSL. If you modify file(s) with this exception, you may extend this exception to your version of the file(s), but you are not obligated to do so. If you do not wish to do so, delete this exception statement from your version. If you delete this exception statement from all source files in the program, then also delete it here. */ #include "config.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #if HAVE_PTY_H #include #elif HAVE_UTIL_H #include #elif HAVE_LIBUTIL_H #include #endif #include "parser.h" #include "swrite.h" #include "locale_utils.h" #include "fatal_assert.h" #include "pty_compat.h" #include "select.h" const size_t buf_size = 1024; static void emulate_terminal( int fd ); static int copy( int src, int dest ); static int vt_parser( int fd, Parser::UTF8Parser *parser ); int main( int argc __attribute__((unused)), char *argv[] __attribute__((unused)), char *envp[] ) { int master; struct termios saved_termios, raw_termios, child_termios; set_native_locale(); fatal_assert( is_utf8_locale() ); if ( tcgetattr( STDIN_FILENO, &saved_termios ) < 0 ) { perror( "tcgetattr" ); exit( 1 ); } child_termios = saved_termios; #ifdef HAVE_IUTF8 if ( !(child_termios.c_iflag & IUTF8) ) { fprintf( stderr, "Warning: Locale is UTF-8 but termios IUTF8 flag not set. Setting IUTF8 flag.\n" ); child_termios.c_iflag |= IUTF8; } #else fprintf( stderr, "Warning: termios IUTF8 flag not defined. Character-erase of multibyte character sequence probably does not work properly on this platform.\n" ); #endif /* HAVE_IUTF8 */ pid_t child = forkpty( &master, NULL, &child_termios, NULL ); if ( child == -1 ) { perror( "forkpty" ); exit( 1 ); } if ( child == 0 ) { /* child */ char *my_argv[ 2 ]; my_argv[ 0 ] = strdup( "/bin/bash" ); assert( my_argv[ 0 ] ); my_argv[ 1 ] = NULL; if ( execve( "/bin/bash", my_argv, envp ) < 0 ) { perror( "execve" ); exit( 1 ); } exit( 0 ); } else { /* parent */ raw_termios = saved_termios; cfmakeraw( &raw_termios ); if ( tcsetattr( STDIN_FILENO, TCSANOW, &raw_termios ) < 0 ) { perror( "tcsetattr" ); exit( 1 ); } emulate_terminal( master ); if ( tcsetattr( STDIN_FILENO, TCSANOW, &saved_termios ) < 0 ) { perror( "tcsetattr" ); exit( 1 ); } } return 0; } static void emulate_terminal( int fd ) { Parser::UTF8Parser parser; Select &sel = Select::get_instance(); sel.add_fd( STDIN_FILENO ); sel.add_fd( fd ); while ( 1 ) { int active_fds = sel.select( -1 ); if ( active_fds <= 0 ) { perror( "select" ); return; } if ( sel.read( STDIN_FILENO ) ) { if ( copy( STDIN_FILENO, fd ) < 0 ) { return; } } else if ( sel.read( fd ) ) { if ( vt_parser( fd, &parser ) < 0 ) { return; } } else if ( sel.error( STDIN_FILENO ) || sel.error( fd ) ) { return; } else { fprintf( stderr, "select mysteriously woken up\n" ); } } } static int copy( int src, int dest ) { char buf[ buf_size ]; ssize_t bytes_read = read( src, buf, buf_size ); if ( bytes_read == 0 ) { /* EOF */ return -1; } else if ( bytes_read < 0 ) { perror( "read" ); return -1; } return swrite( dest, buf, bytes_read ); } static int vt_parser( int fd, Parser::UTF8Parser *parser ) { char buf[ buf_size ]; /* fill buffer if possible */ ssize_t bytes_read = read( fd, buf, buf_size ); if ( bytes_read == 0 ) { /* EOF */ return -1; } else if ( bytes_read < 0 ) { perror( "read" ); return -1; } /* feed to parser */ for ( int i = 0; i < bytes_read; i++ ) { std::list actions = parser->input( buf[ i ] ); for ( std::list::iterator j = actions.begin(); j != actions.end(); j++ ) { Parser::Action *act = *j; assert( act ); if ( act->char_present ) { if ( iswprint( act->ch ) ) { printf( "%s(0x%02x=%lc) ", act->name().c_str(), (unsigned int)act->ch, (wint_t)act->ch ); } else { printf( "%s(0x%02x) ", act->name().c_str(), (unsigned int)act->ch ); } } else { printf( "[%s] ", act->name().c_str() ); } delete act; fflush( stdout ); } } return 0; } mosh-1.2.5/src/examples/termemu.cc000066400000000000000000000212771255051512600170720ustar00rootroot00000000000000/* Mosh: the mobile shell Copyright 2012 Keith Winstein This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . In addition, as a special exception, the copyright holders give permission to link the code of portions of this program with the OpenSSL library under certain conditions as described in each individual source file, and distribute linked combinations including the two. You must obey the GNU General Public License in all respects for all of the code used other than OpenSSL. If you modify file(s) with this exception, you may extend this exception to your version of the file(s), but you are not obligated to do so. If you do not wish to do so, delete this exception statement from your version. If you delete this exception statement from all source files in the program, then also delete it here. */ #include "config.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #if HAVE_PTY_H #include #elif HAVE_UTIL_H #include #elif HAVE_LIBUTIL_H #include #endif #include "parser.h" #include "completeterminal.h" #include "swrite.h" #include "fatal_assert.h" #include "pty_compat.h" #include "locale_utils.h" #include "select.h" const size_t buf_size = 16384; static void emulate_terminal( int fd ); int main( int argc, char *argv[] ) { int master; struct termios saved_termios, raw_termios, child_termios; set_native_locale(); fatal_assert( is_utf8_locale() ); if ( tcgetattr( STDIN_FILENO, &saved_termios ) < 0 ) { perror( "tcgetattr" ); exit( 1 ); } child_termios = saved_termios; #ifdef HAVE_IUTF8 if ( !(child_termios.c_iflag & IUTF8) ) { fprintf( stderr, "Warning: Locale is UTF-8 but termios IUTF8 flag not set. Setting IUTF8 flag.\n" ); child_termios.c_iflag |= IUTF8; } #else fprintf( stderr, "Warning: termios IUTF8 flag not defined. Character-erase of multibyte character sequence probably does not work properly on this platform.\n" ); #endif /* HAVE_IUTF8 */ pid_t child = forkpty( &master, NULL, &child_termios, NULL ); if ( child == -1 ) { perror( "forkpty" ); exit( 1 ); } if ( child == 0 ) { /* child */ if ( setenv( "TERM", "xterm-256color", true ) < 0 ) { perror( "setenv" ); exit( 1 ); } /* ask ncurses to send UTF-8 instead of ISO 2022 for line-drawing chars */ if ( setenv( "NCURSES_NO_UTF8_ACS", "1", true ) < 0 ) { perror( "setenv" ); exit( 1 ); } char *my_argv[ 2 ]; if ( argc > 1 ) { argv++; } else { /* get shell name */ my_argv[ 0 ] = getenv( "SHELL" ); if ( my_argv[ 0 ] == NULL || *my_argv[ 0 ] == '\0' ) { struct passwd *pw = getpwuid( geteuid() ); if ( pw == NULL ) { perror( "getpwuid" ); exit( 1 ); } my_argv[ 0 ] = strdup( pw->pw_shell ); } assert( my_argv[ 0 ] ); my_argv[ 1 ] = NULL; argv = my_argv; } if ( execvp( argv[ 0 ], argv ) < 0 ) { perror( "execve" ); exit( 1 ); } exit( 0 ); } else { /* parent */ raw_termios = saved_termios; cfmakeraw( &raw_termios ); if ( tcsetattr( STDIN_FILENO, TCSANOW, &raw_termios ) < 0 ) { perror( "tcsetattr" ); exit( 1 ); } try { emulate_terminal( master ); } catch ( const std::exception &e ) { fprintf( stderr, "\r\nException caught: %s\r\n", e.what() ); } if ( tcsetattr( STDIN_FILENO, TCSANOW, &saved_termios ) < 0 ) { perror( "tcsetattr" ); exit( 1 ); } } printf( "[stm is exiting.]\n" ); return 0; } /* Print a frame if the last frame was more than 1/50 seconds ago */ static bool tick( Terminal::Framebuffer &state, Terminal::Framebuffer &new_frame, const Terminal::Display &display ) { static bool initialized = false; static struct timeval last_time; struct timeval this_time; if ( gettimeofday( &this_time, NULL ) < 0 ) { perror( "gettimeofday" ); } double diff = (this_time.tv_sec - last_time.tv_sec) + .000001 * (this_time.tv_usec - last_time.tv_usec); if ( (!initialized) || (diff >= 0.02) ) { display.downgrade( new_frame ); std::string update = display.new_frame( initialized, state, new_frame ); swrite( STDOUT_FILENO, update.c_str() ); state = new_frame; initialized = true; last_time = this_time; return true; } return false; } /* This is the main loop. 1. New bytes from the user get applied to the terminal emulator as "UserByte" actions. 2. New bytes from the host get sent to the Parser, and then those actions are applied to the terminal. 3. Resize events (from a SIGWINCH signal) get turned into "Resize" actions and applied to the terminal. At every event from select(), we run the tick() function to possibly print a new frame (if we haven't printed one in the last 1/50 second). The new frames are "differential" -- they assume the previous frame was sent to the real terminal. */ static void emulate_terminal( int fd ) { /* get current window size */ struct winsize window_size; if ( ioctl( STDIN_FILENO, TIOCGWINSZ, &window_size ) < 0 ) { perror( "ioctl TIOCGWINSZ" ); return; } /* tell child process */ if ( ioctl( fd, TIOCSWINSZ, &window_size ) < 0 ) { perror( "ioctl TIOCSWINSZ" ); return; } /* open parser and terminal */ Terminal::Complete complete( window_size.ws_col, window_size.ws_row ); Terminal::Framebuffer state( window_size.ws_col, window_size.ws_row ); /* open display */ Terminal::Display display( true ); /* use TERM to initialize */ Select &sel = Select::get_instance(); sel.add_fd( STDIN_FILENO ); sel.add_fd( fd ); sel.add_signal( SIGWINCH ); swrite( STDOUT_FILENO, display.open().c_str() ); int timeout = -1; while ( 1 ) { int active_fds = sel.select( timeout ); if ( active_fds < 0 ) { perror( "select" ); break; } if ( sel.read( STDIN_FILENO ) ) { /* input from user */ char buf[ buf_size ]; /* fill buffer if possible */ ssize_t bytes_read = read( STDIN_FILENO, buf, buf_size ); if ( bytes_read == 0 ) { /* EOF */ return; } else if ( bytes_read < 0 ) { perror( "read" ); return; } std::string terminal_to_host; for ( int i = 0; i < bytes_read; i++ ) { Parser::UserByte ub( buf[ i ] ); terminal_to_host += complete.act( &ub ); } if ( swrite( fd, terminal_to_host.c_str(), terminal_to_host.length() ) < 0 ) { break; } } else if ( sel.read( fd ) ) { /* input from host */ char buf[ buf_size ]; /* fill buffer if possible */ ssize_t bytes_read = read( fd, buf, buf_size ); if ( bytes_read == 0 ) { /* EOF */ return; } else if ( bytes_read < 0 ) { perror( "read" ); return; } std::string terminal_to_host = complete.act( std::string( buf, bytes_read ) ); if ( swrite( fd, terminal_to_host.c_str(), terminal_to_host.length() ) < 0 ) { break; } } else if ( sel.signal( SIGWINCH ) ) { /* get new size */ if ( ioctl( STDIN_FILENO, TIOCGWINSZ, &window_size ) < 0 ) { perror( "ioctl TIOCGWINSZ" ); return; } /* tell emulator */ Parser::Resize r( window_size.ws_col, window_size.ws_row ); complete.act( &r ); /* tell child process */ if ( ioctl( fd, TIOCSWINSZ, &window_size ) < 0 ) { perror( "ioctl TIOCSWINSZ" ); return; } } else if ( sel.error( STDIN_FILENO ) || sel.error( fd ) ) { break; } Terminal::Framebuffer new_frame( complete.get_fb() ); if ( tick( state, new_frame, display ) ) { /* there was a frame */ timeout = -1; } else { timeout = 20; } } std::string update = display.new_frame( true, state, complete.get_fb() ); swrite( STDOUT_FILENO, update.c_str() ); swrite( STDOUT_FILENO, display.close().c_str() ); } mosh-1.2.5/src/frontend/000077500000000000000000000000001255051512600150755ustar00rootroot00000000000000mosh-1.2.5/src/frontend/.gitignore000066400000000000000000000000321255051512600170600ustar00rootroot00000000000000/mosh-client /mosh-server mosh-1.2.5/src/frontend/Makefile.am000066400000000000000000000015101255051512600171260ustar00rootroot00000000000000AM_CPPFLAGS = -I$(srcdir)/../statesync -I$(srcdir)/../terminal -I$(srcdir)/../network -I$(srcdir)/../crypto -I../protobufs -I$(srcdir)/../util $(TINFO_CFLAGS) $(protobuf_CFLAGS) $(OPENSSL_CFLAGS) AM_CXXFLAGS = $(WARNING_CXXFLAGS) $(PICKY_CXXFLAGS) $(HARDEN_CFLAGS) $(MISC_CXXFLAGS) AM_LDFLAGS = $(HARDEN_LDFLAGS) LDADD = ../crypto/libmoshcrypto.a ../network/libmoshnetwork.a ../statesync/libmoshstatesync.a ../terminal/libmoshterminal.a ../util/libmoshutil.a ../protobufs/libmoshprotos.a -lm $(TINFO_LIBS) $(protobuf_LIBS) $(OPENSSL_LIBS) mosh_server_LDADD = $(LDADD) $(LIBUTIL) bin_PROGRAMS = if BUILD_CLIENT bin_PROGRAMS += mosh-client endif if BUILD_SERVER bin_PROGRAMS += mosh-server endif mosh_client_SOURCES = mosh-client.cc stmclient.cc stmclient.h terminaloverlay.cc terminaloverlay.h mosh_server_SOURCES = mosh-server.cc mosh-1.2.5/src/frontend/mosh-client.cc000066400000000000000000000123561255051512600176350ustar00rootroot00000000000000/* Mosh: the mobile shell Copyright 2012 Keith Winstein This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . In addition, as a special exception, the copyright holders give permission to link the code of portions of this program with the OpenSSL library under certain conditions as described in each individual source file, and distribute linked combinations including the two. You must obey the GNU General Public License in all respects for all of the code used other than OpenSSL. If you modify file(s) with this exception, you may extend this exception to your version of the file(s), but you are not obligated to do so. If you do not wish to do so, delete this exception statement from your version. If you delete this exception statement from all source files in the program, then also delete it here. */ #include "config.h" #include "version.h" #include #include #include "stmclient.h" #include "crypto.h" #include "locale_utils.h" #include "fatal_assert.h" /* These need to be included last because of conflicting defines. */ /* * stmclient.h includes termios.h, and that will break termio/termios pull in on Solaris. * The solution is to include termio.h also. * But Mac OS X doesn't have termio.h, so this needs a guard. */ #ifdef HAVE_TERMIO_H #include #endif #if defined HAVE_NCURSESW_CURSES_H # include # include #elif defined HAVE_NCURSESW_H # include # include #elif defined HAVE_NCURSES_CURSES_H # include # include #elif defined HAVE_NCURSES_H # include # include #elif defined HAVE_CURSES_H # include # include #else # error "SysV or X/Open-compatible Curses header file required" #endif static void usage( const char *argv0 ) { fprintf( stderr, "mosh-client (%s) [build %s]\n", PACKAGE_STRING, BUILD_VERSION ); fprintf( stderr, "Copyright 2012 Keith Winstein \n" ); fprintf( stderr, "License GPLv3+: GNU GPL version 3 or later .\nThis is free software: you are free to change and redistribute it.\nThere is NO WARRANTY, to the extent permitted by law.\n\n" ); fprintf( stderr, "Usage: %s IP PORT\n %s -c\n", argv0, argv0 ); } static void print_colorcount( void ) { /* check colors */ setupterm((char *)0, 1, (int *)0); char colors_name[] = "colors"; int color_val = tigetnum( colors_name ); if ( color_val == -2 ) { fprintf( stderr, "Invalid terminfo numeric capability: %s\n", colors_name ); } printf( "%d\n", color_val ); } #ifdef NACL int mosh_main( int argc, char *argv[] ) #else int main( int argc, char *argv[] ) #endif { /* For security, make sure we don't dump core */ Crypto::disable_dumping_core(); /* Detect edge case */ fatal_assert( argc > 0 ); /* Get arguments */ int opt; while ( (opt = getopt( argc, argv, "c" )) != -1 ) { switch ( opt ) { case 'c': print_colorcount(); exit( 0 ); break; default: usage( argv[ 0 ] ); exit( 1 ); break; } } char *ip, *desired_port; if ( argc - optind != 2 ) { usage( argv[ 0 ] ); exit( 1 ); } ip = argv[ optind ]; desired_port = argv[ optind + 1 ]; /* Sanity-check arguments */ if ( desired_port && ( strspn( desired_port, "0123456789" ) != strlen( desired_port ) ) ) { fprintf( stderr, "%s: Bad UDP port (%s)\n\n", argv[ 0 ], desired_port ); usage( argv[ 0 ] ); exit( 1 ); } /* Read key from environment */ char *env_key = getenv( "MOSH_KEY" ); if ( env_key == NULL ) { fprintf( stderr, "MOSH_KEY environment variable not found.\n" ); exit( 1 ); } /* Read prediction preference */ char *predict_mode = getenv( "MOSH_PREDICTION_DISPLAY" ); /* can be NULL */ char *key = strdup( env_key ); if ( key == NULL ) { perror( "strdup" ); exit( 1 ); } if ( unsetenv( "MOSH_KEY" ) < 0 ) { perror( "unsetenv" ); exit( 1 ); } /* Adopt native locale */ set_native_locale(); try { STMClient client( ip, desired_port, key, predict_mode ); client.init(); try { client.main(); } catch ( ... ) { client.shutdown(); throw; } client.shutdown(); } catch ( const Network::NetworkException &e ) { fprintf( stderr, "Network exception: %s\r\n", e.what() ); } catch ( const Crypto::CryptoException &e ) { fprintf( stderr, "Crypto exception: %s\r\n", e.what() ); } catch ( const std::exception &e ) { fprintf( stderr, "Error: %s\r\n", e.what() ); } printf( "\n[mosh is exiting.]\n" ); free( key ); return 0; } mosh-1.2.5/src/frontend/mosh-server.cc000066400000000000000000000566241255051512600176730ustar00rootroot00000000000000/* Mosh: the mobile shell Copyright 2012 Keith Winstein This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . In addition, as a special exception, the copyright holders give permission to link the code of portions of this program with the OpenSSL library under certain conditions as described in each individual source file, and distribute linked combinations including the two. You must obey the GNU General Public License in all respects for all of the code used other than OpenSSL. If you modify file(s) with this exception, you may extend this exception to your version of the file(s), but you are not obligated to do so. If you do not wish to do so, delete this exception statement from your version. If you delete this exception statement from all source files in the program, then also delete it here. */ #include "config.h" #include "version.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #ifdef HAVE_UTEMPTER #include #endif #include #include #include #include #ifdef HAVE_UTMPX_H #include #endif #ifdef HAVE_PATHS_H #include #endif #if HAVE_PTY_H #include #elif HAVE_UTIL_H #include #endif #if FORKPTY_IN_LIBUTIL #include #endif #include "completeterminal.h" #include "swrite.h" #include "user.h" #include "fatal_assert.h" #include "locale_utils.h" #include "pty_compat.h" #include "select.h" #include "timestamp.h" #include "fatal_assert.h" #ifndef _PATH_BSHELL #define _PATH_BSHELL "/bin/sh" #endif #include "networktransport.cc" typedef Network::Transport< Terminal::Complete, Network::UserStream > ServerConnection; static void serve( int host_fd, Terminal::Complete &terminal, ServerConnection &network ); static int run_server( const char *desired_ip, const char *desired_port, const string &command_path, char *command_argv[], const int colors, bool verbose, bool with_motd ); using namespace std; static void print_usage( const char *argv0 ) { fprintf( stderr, "Usage: %s new [-s] [-v] [-i LOCALADDR] [-p PORT[:PORT2]] [-c COLORS] [-l NAME=VALUE] [-- COMMAND...]\n", argv0 ); } static void print_motd( void ); static void chdir_homedir( void ); static bool motd_hushed( void ); static void warn_unattached( const string & ignore_entry ); /* Simple spinloop */ static void spin( void ) { static unsigned int spincount = 0; spincount++; if ( spincount > 10 ) { struct timespec req; req.tv_sec = 0; req.tv_nsec = 100000000; /* 0.1 sec */ nanosleep( &req, NULL ); freeze_timestamp(); } } static string get_SSH_IP( void ) { const char *SSH_CONNECTION = getenv( "SSH_CONNECTION" ); if ( !SSH_CONNECTION ) { /* Older sshds don't set this */ fprintf( stderr, "Warning: SSH_CONNECTION not found; binding to any interface.\n" ); return string( "0.0.0.0" ); } istringstream ss( SSH_CONNECTION ); string dummy, local_interface_IP; ss >> dummy >> dummy >> local_interface_IP; if ( !ss ) { fprintf( stderr, "Warning: Could not parse SSH_CONNECTION; binding to any interface.\n" ); return string( "0.0.0.0" ); } /* Strip IPv6 prefix. */ const char IPv6_prefix[] = "::ffff:"; if ( ( local_interface_IP.length() > strlen( IPv6_prefix ) ) && ( 0 == strncasecmp( local_interface_IP.c_str(), IPv6_prefix, strlen( IPv6_prefix ) ) ) ) { return local_interface_IP.substr( strlen( IPv6_prefix ) ); } return local_interface_IP; } int main( int argc, char *argv[] ) { /* For security, make sure we don't dump core */ Crypto::disable_dumping_core(); /* Detect edge case */ fatal_assert( argc > 0 ); const char *desired_ip = NULL; string desired_ip_str; const char *desired_port = NULL; string command_path; char **command_argv = NULL; int colors = 0; bool verbose = false; /* don't close stdin/stdout/stderr */ /* Will cause mosh-server not to correctly detach on old versions of sshd. */ list locale_vars; /* strip off command */ for ( int i = 0; i < argc; i++ ) { if ( 0 == strcmp( argv[ i ], "--" ) ) { /* -- is mandatory */ if ( i != argc - 1 ) { command_argv = argv + i + 1; } argc = i; /* rest of options before -- */ break; } } /* Parse new command-line syntax */ if ( (argc >= 2) && (strcmp( argv[ 1 ], "new" ) == 0) ) { /* new option syntax */ int opt; while ( (opt = getopt( argc - 1, argv + 1, "i:p:c:svl:" )) != -1 ) { switch ( opt ) { case 'i': desired_ip = optarg; break; case 'p': desired_port = optarg; break; case 's': desired_ip_str = get_SSH_IP(); desired_ip = desired_ip_str.c_str(); fatal_assert( desired_ip ); break; case 'c': try { colors = myatoi( optarg ); } catch ( const CryptoException & ) { fprintf( stderr, "%s: Bad number of colors (%s)\n", argv[ 0 ], optarg ); print_usage( argv[ 0 ] ); exit( 1 ); } break; case 'v': verbose = true; break; case 'l': locale_vars.push_back( string( optarg ) ); break; default: print_usage( argv[ 0 ] ); /* don't die on unknown options */ } } } else if ( argc == 1 ) { /* legacy argument parsing for older client wrapper script */ /* do nothing */ } else if ( argc == 2 ) { desired_ip = argv[ 1 ]; } else if ( argc == 3 ) { desired_ip = argv[ 1 ]; desired_port = argv[ 2 ]; } else { print_usage( argv[ 0 ] ); exit( 1 ); } /* Sanity-check arguments */ int dpl, dph; if ( desired_port && ! Connection::parse_portrange( desired_port, dpl, dph ) ) { fprintf( stderr, "%s: Bad UDP port range (%s)\n", argv[ 0 ], desired_port ); print_usage( argv[ 0 ] ); exit( 1 ); } bool with_motd = false; /* Get shell */ char *my_argv[ 2 ]; string shell_name; if ( !command_argv ) { /* get shell name */ const char *shell = getenv( "SHELL" ); if ( shell == NULL ) { struct passwd *pw = getpwuid( geteuid() ); if ( pw == NULL ) { perror( "getpwuid" ); exit( 1 ); } shell = pw->pw_shell; } string shell_path( shell ); if ( shell_path.empty() ) { /* empty shell means Bourne shell */ shell_path = _PATH_BSHELL; } command_path = shell_path; size_t shell_slash( shell_path.rfind('/') ); if ( shell_slash == string::npos ) { shell_name = shell_path; } else { shell_name = shell_path.substr(shell_slash + 1); } /* prepend '-' to make login shell */ shell_name = '-' + shell_name; my_argv[ 0 ] = const_cast( shell_name.c_str() ); my_argv[ 1 ] = NULL; command_argv = my_argv; with_motd = true; } if ( command_path.empty() ) { command_path = command_argv[0]; } /* Adopt implementation locale */ set_native_locale(); if ( !is_utf8_locale() ) { /* save details for diagnostic */ LocaleVar native_ctype = get_ctype(); string native_charset( locale_charset() ); /* apply locale-related environment variables from client */ clear_locale_variables(); for ( list::const_iterator i = locale_vars.begin(); i != locale_vars.end(); i++ ) { char *env_string = strdup( i->c_str() ); fatal_assert( env_string ); if ( 0 != putenv( env_string ) ) { perror( "putenv" ); } } /* check again */ set_native_locale(); if ( !is_utf8_locale() ) { LocaleVar client_ctype = get_ctype(); string client_charset( locale_charset() ); fprintf( stderr, "mosh-server needs a UTF-8 native locale to run.\n\n" ); fprintf( stderr, "Unfortunately, the local environment (%s) specifies\nthe character set \"%s\",\n\n", native_ctype.str().c_str(), native_charset.c_str() ); fprintf( stderr, "The client-supplied environment (%s) specifies\nthe character set \"%s\".\n\n", client_ctype.str().c_str(), client_charset.c_str() ); int unused __attribute((unused)) = system( "locale" ); exit( 1 ); } } try { return run_server( desired_ip, desired_port, command_path, command_argv, colors, verbose, with_motd ); } catch ( const Network::NetworkException &e ) { fprintf( stderr, "Network exception: %s\n", e.what() ); return 1; } catch ( const Crypto::CryptoException &e ) { fprintf( stderr, "Crypto exception: %s\n", e.what() ); return 1; } } static int run_server( const char *desired_ip, const char *desired_port, const string &command_path, char *command_argv[], const int colors, bool verbose, bool with_motd ) { /* get initial window size */ struct winsize window_size; if ( ioctl( STDIN_FILENO, TIOCGWINSZ, &window_size ) < 0 || window_size.ws_col == 0 || window_size.ws_row == 0 ) { fprintf( stderr, "Server started without pseudo-terminal. Opening 80x24 terminal.\n" ); /* Fill in sensible defaults. */ /* They will be overwritten by client on first connection. */ memset( &window_size, 0, sizeof( window_size ) ); window_size.ws_col = 80; window_size.ws_row = 24; } /* open parser and terminal */ Terminal::Complete terminal( window_size.ws_col, window_size.ws_row ); /* open network */ Network::UserStream blank; ServerConnection *network = new ServerConnection( terminal, blank, desired_ip, desired_port ); if ( verbose ) { network->set_verbose(); } printf( "\nMOSH CONNECT %s %s\n", network->port().c_str(), network->get_key().c_str() ); fflush( stdout ); /* don't let signals kill us */ struct sigaction sa; sa.sa_handler = SIG_IGN; sa.sa_flags = 0; fatal_assert( 0 == sigfillset( &sa.sa_mask ) ); fatal_assert( 0 == sigaction( SIGHUP, &sa, NULL ) ); fatal_assert( 0 == sigaction( SIGPIPE, &sa, NULL ) ); /* detach from terminal */ pid_t the_pid = fork(); if ( the_pid < 0 ) { perror( "fork" ); } else if ( the_pid > 0 ) { _exit( 0 ); } fprintf( stderr, "\nmosh-server (%s) [build %s]\n", PACKAGE_STRING, BUILD_VERSION ); fprintf( stderr, "Copyright 2012 Keith Winstein \n" ); fprintf( stderr, "License GPLv3+: GNU GPL version 3 or later .\nThis is free software: you are free to change and redistribute it.\nThere is NO WARRANTY, to the extent permitted by law.\n\n" ); fprintf( stderr, "[mosh-server detached, pid = %d]\n", (int)getpid() ); int master; #ifndef HAVE_IUTF8 fprintf( stderr, "\nWarning: termios IUTF8 flag not defined.\nCharacter-erase of multibyte character sequence\nprobably does not work properly on this platform.\n" ); #endif /* HAVE_IUTF8 */ /* close file descriptors */ if ( !verbose ) { /* Necessary to properly detach on old versions of sshd (e.g. RHEL/CentOS 5.0). */ int nullfd; nullfd = open( "/dev/null", O_RDWR ); if ( nullfd == -1 ) { perror( "open" ); exit( 1 ); } if ( dup2 ( nullfd, STDIN_FILENO ) < 0 || dup2 ( nullfd, STDOUT_FILENO ) < 0 || dup2 ( nullfd, STDERR_FILENO ) < 0 ) { perror( "dup2" ); exit( 1 ); } if ( close( nullfd ) < 0 ) { perror( "close" ); exit( 1 ); } } char utmp_entry[ 64 ] = { 0 }; snprintf( utmp_entry, 64, "mosh [%d]", getpid() ); /* Fork child process */ pid_t child = forkpty( &master, NULL, NULL, &window_size ); if ( child == -1 ) { perror( "forkpty" ); exit( 1 ); } if ( child == 0 ) { /* child */ /* reenable signals */ struct sigaction sa; sa.sa_handler = SIG_DFL; sa.sa_flags = 0; fatal_assert( 0 == sigfillset( &sa.sa_mask ) ); fatal_assert( 0 == sigaction( SIGHUP, &sa, NULL ) ); fatal_assert( 0 == sigaction( SIGPIPE, &sa, NULL ) ); /* close server-related file descriptors */ delete network; /* set IUTF8 if available */ #ifdef HAVE_IUTF8 struct termios child_termios; if ( tcgetattr( STDIN_FILENO, &child_termios ) < 0 ) { perror( "tcgetattr" ); exit( 1 ); } child_termios.c_iflag |= IUTF8; if ( tcsetattr( STDIN_FILENO, TCSANOW, &child_termios ) < 0 ) { perror( "tcsetattr" ); exit( 1 ); } #endif /* HAVE_IUTF8 */ /* set TERM */ const char default_term[] = "xterm"; const char color_term[] = "xterm-256color"; if ( setenv( "TERM", (colors == 256) ? color_term : default_term, true ) < 0 ) { perror( "setenv" ); exit( 1 ); } /* ask ncurses to send UTF-8 instead of ISO 2022 for line-drawing chars */ if ( setenv( "NCURSES_NO_UTF8_ACS", "1", true ) < 0 ) { perror( "setenv" ); exit( 1 ); } /* clear STY environment variable so GNU screen regards us as top level */ if ( unsetenv( "STY" ) < 0 ) { perror( "unsetenv" ); exit( 1 ); } chdir_homedir(); if ( with_motd && (!motd_hushed()) ) { print_motd(); warn_unattached( utmp_entry ); } Crypto::reenable_dumping_core(); if ( execvp( command_path.c_str(), command_argv ) < 0 ) { perror( "execvp" ); _exit( 1 ); } } else { /* parent */ #ifdef HAVE_UTEMPTER /* make utmp entry */ utempter_add_record( master, utmp_entry ); #endif try { serve( master, terminal, *network ); } catch ( const Network::NetworkException &e ) { fprintf( stderr, "Network exception: %s\n", e.what() ); } catch ( const Crypto::CryptoException &e ) { fprintf( stderr, "Crypto exception: %s\n", e.what() ); } #ifdef HAVE_UTEMPTER utempter_remove_record( master ); #endif if ( close( master ) < 0 ) { perror( "close" ); exit( 1 ); } delete network; } printf( "\n[mosh-server is exiting.]\n" ); return 0; } static void serve( int host_fd, Terminal::Complete &terminal, ServerConnection &network ) { /* prepare to poll for events */ Select &sel = Select::get_instance(); sel.add_signal( SIGTERM ); sel.add_signal( SIGINT ); uint64_t last_remote_num = network.get_remote_state_num(); #ifdef HAVE_UTEMPTER bool connected_utmp = false; Addr saved_addr; socklen_t saved_addr_len = 0; #endif while ( 1 ) { try { uint64_t now = Network::timestamp(); const int timeout_if_no_client = 60000; int timeout = min( network.wait_time(), terminal.wait_time( now ) ); if ( (!network.get_remote_state_num()) || network.shutdown_in_progress() ) { timeout = min( timeout, 5000 ); } /* poll for events */ sel.clear_fds(); std::vector< int > fd_list( network.fds() ); assert( fd_list.size() == 1 ); /* servers don't hop */ int network_fd = fd_list.back(); sel.add_fd( network_fd ); if ( !network.shutdown_in_progress() ) { sel.add_fd( host_fd ); } int active_fds = sel.select( timeout ); if ( active_fds < 0 ) { perror( "select" ); break; } now = Network::timestamp(); uint64_t time_since_remote_state = now - network.get_latest_remote_state().timestamp; if ( sel.read( network_fd ) ) { /* packet received from the network */ network.recv(); /* is new user input available for the terminal? */ if ( network.get_remote_state_num() != last_remote_num ) { last_remote_num = network.get_remote_state_num(); string terminal_to_host; Network::UserStream us; us.apply_string( network.get_remote_diff() ); /* apply userstream to terminal */ for ( size_t i = 0; i < us.size(); i++ ) { const Parser::Action *action = us.get_action( i ); terminal_to_host += terminal.act( action ); if ( typeid( *action ) == typeid( Parser::Resize ) ) { /* tell child process of resize */ const Parser::Resize *res = static_cast( us.get_action( i ) ); struct winsize window_size; if ( ioctl( host_fd, TIOCGWINSZ, &window_size ) < 0 ) { perror( "ioctl TIOCGWINSZ" ); return; } window_size.ws_col = res->width; window_size.ws_row = res->height; if ( ioctl( host_fd, TIOCSWINSZ, &window_size ) < 0 ) { perror( "ioctl TIOCSWINSZ" ); return; } } } if ( !us.empty() ) { /* register input frame number for future echo ack */ terminal.register_input_frame( last_remote_num, now ); } /* update client with new state of terminal */ if ( !network.shutdown_in_progress() ) { network.set_current_state( terminal ); } /* write any writeback octets back to the host */ if ( swrite( host_fd, terminal_to_host.c_str(), terminal_to_host.length() ) < 0 ) { break; } #ifdef HAVE_UTEMPTER /* update utmp entry if we have become "connected" */ if ( (!connected_utmp) || saved_addr_len != network.get_remote_addr_len() || memcmp( &saved_addr, &network.get_remote_addr(), saved_addr_len ) != 0 ) { utempter_remove_record( host_fd ); saved_addr = network.get_remote_addr(); saved_addr_len = network.get_remote_addr_len(); char host[ NI_MAXHOST ]; int errcode = getnameinfo( &saved_addr.sa, saved_addr_len, host, sizeof( host ), NULL, 0, NI_NUMERICHOST ); if ( errcode != 0 ) { throw NetworkException( std::string( "serve: getnameinfo: " ) + gai_strerror( errcode ), 0 ); } char tmp[ 64 ]; snprintf( tmp, 64, "%s via mosh [%d]", host, getpid() ); utempter_add_record( host_fd, tmp ); connected_utmp = true; } #endif } } if ( (!network.shutdown_in_progress()) && sel.read( host_fd ) ) { /* input from the host needs to be fed to the terminal */ const int buf_size = 16384; char buf[ buf_size ]; /* fill buffer if possible */ ssize_t bytes_read = read( host_fd, buf, buf_size ); /* If the pty slave is closed, reading from the master can fail with EIO (see #264). So we treat errors on read() like EOF. */ if ( bytes_read <= 0 ) { network.start_shutdown(); } else { string terminal_to_host = terminal.act( string( buf, bytes_read ) ); /* update client with new state of terminal */ network.set_current_state( terminal ); /* write any writeback octets back to the host */ if ( swrite( host_fd, terminal_to_host.c_str(), terminal_to_host.length() ) < 0 ) { break; } } } if ( sel.any_signal() ) { /* shutdown signal */ if ( network.has_remote_addr() && (!network.shutdown_in_progress()) ) { network.start_shutdown(); } else { break; } } if ( sel.error( network_fd ) ) { /* network problem */ break; } if ( (!network.shutdown_in_progress()) && sel.error( host_fd ) ) { /* host problem */ network.start_shutdown(); } /* quit if our shutdown has been acknowledged */ if ( network.shutdown_in_progress() && network.shutdown_acknowledged() ) { break; } /* quit after shutdown acknowledgement timeout */ if ( network.shutdown_in_progress() && network.shutdown_ack_timed_out() ) { break; } /* quit if we received and acknowledged a shutdown request */ if ( network.counterparty_shutdown_ack_sent() ) { break; } #ifdef HAVE_UTEMPTER /* update utmp if has been more than 30 seconds since heard from client */ if ( connected_utmp ) { if ( time_since_remote_state > 30000 ) { utempter_remove_record( host_fd ); char tmp[ 64 ]; snprintf( tmp, 64, "mosh [%d]", getpid() ); utempter_add_record( host_fd, tmp ); connected_utmp = false; } } #endif if ( terminal.set_echo_ack( now ) ) { /* update client with new echo ack */ if ( !network.shutdown_in_progress() ) { network.set_current_state( terminal ); } } if ( !network.get_remote_state_num() && time_since_remote_state >= uint64_t( timeout_if_no_client ) ) { fprintf( stderr, "No connection within %d seconds.\n", timeout_if_no_client / 1000 ); break; } network.tick(); } catch ( const Network::NetworkException &e ) { fprintf( stderr, "%s\n", e.what() ); spin(); } catch ( const Crypto::CryptoException &e ) { if ( e.fatal ) { throw; } else { fprintf( stderr, "Crypto exception: %s\n", e.what() ); } } } } /* OpenSSH prints the motd on startup, so we will too */ static void print_motd( void ) { FILE *motd = fopen( "/etc/motd", "r" ); if ( !motd ) { return; /* don't report error on missing or forbidden motd */ } const int BUFSIZE = 256; char buffer[ BUFSIZE ]; while ( 1 ) { size_t bytes_read = fread( buffer, 1, BUFSIZE, motd ); if ( bytes_read == 0 ) { break; /* don't report error */ } size_t bytes_written = fwrite( buffer, 1, bytes_read, stdout ); if ( bytes_written == 0 ) { break; } } fclose( motd ); } static void chdir_homedir( void ) { const char *home = getenv( "HOME" ); if ( home == NULL ) { struct passwd *pw = getpwuid( geteuid() ); if ( pw == NULL ) { perror( "getpwuid" ); return; /* non-fatal */ } home = pw->pw_dir; } if ( chdir( home ) < 0 ) { perror( "chdir" ); } if ( setenv( "PWD", home, 1 ) < 0 ) { perror( "setenv" ); } } static bool motd_hushed( void ) { /* must be in home directory already */ struct stat buf; return (0 == lstat( ".hushlogin", &buf )); } static bool device_exists( const char *ut_line ) { string device_name = string( "/dev/" ) + string( ut_line ); struct stat buf; return (0 == lstat( device_name.c_str(), &buf )); } static void warn_unattached( const string & ignore_entry ) { #ifdef HAVE_UTMPX_H /* get username */ const struct passwd *pw = getpwuid( geteuid() ); if ( pw == NULL ) { perror( "getpwuid" ); /* non-fatal */ return; } const string username( pw->pw_name ); /* look for unattached sessions */ vector< string > unattached_mosh_servers; while ( struct utmpx *entry = getutxent() ) { if ( (entry->ut_type == USER_PROCESS) && (username == string( entry->ut_user )) ) { /* does line show unattached mosh session */ string text( entry->ut_host ); if ( (text.size() >= 5) && (text.substr( 0, 5 ) == "mosh ") && (text[ text.size() - 1 ] == ']') && (text != ignore_entry) && device_exists( entry->ut_line ) ) { unattached_mosh_servers.push_back( text ); } } } /* print out warning if necessary */ if ( unattached_mosh_servers.empty() ) { return; } else if ( unattached_mosh_servers.size() == 1 ) { printf( "\033[37;44mMosh: You have a detached Mosh session on this server (%s).\033[m\n\n", unattached_mosh_servers.front().c_str() ); } else { string pid_string; for ( vector< string >::const_iterator it = unattached_mosh_servers.begin(); it != unattached_mosh_servers.end(); it++ ) { pid_string += " - " + *it + "\n"; } printf( "\033[37;44mMosh: You have %d detached Mosh sessions on this server, with PIDs:\n%s\033[m\n", (int)unattached_mosh_servers.size(), pid_string.c_str() ); } #endif /* HAVE_UTMPX_H */ } mosh-1.2.5/src/frontend/stmclient.cc000066400000000000000000000440321255051512600174110ustar00rootroot00000000000000/* Mosh: the mobile shell Copyright 2012 Keith Winstein This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . In addition, as a special exception, the copyright holders give permission to link the code of portions of this program with the OpenSSL library under certain conditions as described in each individual source file, and distribute linked combinations including the two. You must obey the GNU General Public License in all respects for all of the code used other than OpenSSL. If you modify file(s) with this exception, you may extend this exception to your version of the file(s), but you are not obligated to do so. If you do not wish to do so, delete this exception statement from your version. If you delete this exception statement from all source files in the program, then also delete it here. */ #include "config.h" #include #include #include #include #include #include #include #include #include #include #include #if HAVE_PTY_H #include #elif HAVE_UTIL_H #include #endif #include "stmclient.h" #include "swrite.h" #include "completeterminal.h" #include "user.h" #include "fatal_assert.h" #include "locale_utils.h" #include "pty_compat.h" #include "select.h" #include "timestamp.h" #include "networktransport.cc" void STMClient::resume( void ) { /* Restore termios state */ if ( tcsetattr( STDIN_FILENO, TCSANOW, &raw_termios ) < 0 ) { perror( "tcsetattr" ); exit( 1 ); } /* Put terminal in application-cursor-key mode */ swrite( STDOUT_FILENO, display.open().c_str() ); /* Flag that outer terminal state is unknown */ repaint_requested = true; } void STMClient::init( void ) { if ( !is_utf8_locale() ) { LocaleVar native_ctype = get_ctype(); string native_charset( locale_charset() ); fprintf( stderr, "mosh-client needs a UTF-8 native locale to run.\n\n" ); fprintf( stderr, "Unfortunately, the client's environment (%s) specifies\nthe character set \"%s\".\n\n", native_ctype.str().c_str(), native_charset.c_str() ); int unused __attribute((unused)) = system( "locale" ); exit( 1 ); } /* Verify terminal configuration */ if ( tcgetattr( STDIN_FILENO, &saved_termios ) < 0 ) { perror( "tcgetattr" ); exit( 1 ); } /* Put terminal driver in raw mode */ raw_termios = saved_termios; #ifdef HAVE_IUTF8 if ( !(raw_termios.c_iflag & IUTF8) ) { // fprintf( stderr, "Warning: Locale is UTF-8 but termios IUTF8 flag not set. Setting IUTF8 flag.\n" ); /* Probably not really necessary since we are putting terminal driver into raw mode anyway. */ raw_termios.c_iflag |= IUTF8; } #endif /* HAVE_IUTF8 */ cfmakeraw( &raw_termios ); if ( tcsetattr( STDIN_FILENO, TCSANOW, &raw_termios ) < 0 ) { perror( "tcsetattr" ); exit( 1 ); } /* Put terminal in application-cursor-key mode */ swrite( STDOUT_FILENO, display.open().c_str() ); /* Add our name to window title */ if ( !getenv( "MOSH_TITLE_NOPREFIX" ) ) { overlays.set_title_prefix( wstring( L"[mosh] " ) ); } /* Set terminal escape key. */ const char *escape_key_env; if ( (escape_key_env = getenv( "MOSH_ESCAPE_KEY" )) != NULL ) { if ( strlen( escape_key_env ) == 1 ) { escape_key = (int)escape_key_env[0]; if ( escape_key > 0 && escape_key < 128 ) { if ( escape_key < 32 ) { /* If escape is ctrl-something, pass it with repeating the key without ctrl. */ escape_pass_key = escape_key + (int)'@'; } else { /* If escape is something else, pass it with repeating the key itself. */ escape_pass_key = escape_key; } if ( escape_pass_key >= 'A' && escape_pass_key <= 'Z' ) { /* If escape pass is an upper case character, define optional version as lower case of the same. */ escape_pass_key2 = escape_pass_key + (int)'a' - (int)'A'; } else { escape_pass_key2 = escape_pass_key; } } else { escape_key = 0x1E; escape_pass_key = '^'; escape_pass_key2 = '^'; } } else if ( strlen( escape_key_env ) == 0 ) { escape_key = -1; } else { escape_key = 0x1E; escape_pass_key = '^'; escape_pass_key2 = '^'; } } else { escape_key = 0x1E; escape_pass_key = '^'; escape_pass_key2 = '^'; } /* There are so many better ways to shoot oneself into leg than setting escape key to Ctrl-C, Ctrl-D, NewLine, Ctrl-L or CarriageReturn that we just won't allow that. */ if ( escape_key == 0x03 || escape_key == 0x04 || escape_key == 0x0A || escape_key == 0x0C || escape_key == 0x0D ) { escape_key = 0x1E; escape_pass_key = '^'; escape_pass_key2 = '^'; } /* Adjust escape help differently if escape is a control character. */ if ( escape_key > 0 ) { char escape_pass_name_buf[16]; char escape_key_name_buf[16]; snprintf(escape_pass_name_buf, sizeof escape_pass_name_buf, "\"%c\"", escape_pass_key); if (escape_key < 32) { snprintf(escape_key_name_buf, sizeof escape_key_name_buf, "Ctrl-%c", escape_pass_key); escape_requires_lf = false; } else { snprintf(escape_key_name_buf, sizeof escape_key_name_buf, "\"%c\"", escape_key); escape_requires_lf = true; } string tmp; tmp = string( escape_pass_name_buf ); wstring escape_pass_name = std::wstring(tmp.begin(), tmp.end()); tmp = string( escape_key_name_buf ); wstring escape_key_name = std::wstring(tmp.begin(), tmp.end()); escape_key_help = L"Commands: Ctrl-Z suspends, \".\" quits, " + escape_pass_name + L" gives literal " + escape_key_name; overlays.get_notification_engine().set_escape_key_string( tmp ); } wchar_t tmp[ 128 ]; swprintf( tmp, 128, L"Nothing received from server on UDP port %s.", port.c_str() ); connecting_notification = wstring( tmp ); } void STMClient::shutdown( void ) { /* Restore screen state */ overlays.get_notification_engine().set_notification_string( wstring( L"" ) ); overlays.get_notification_engine().server_heard( timestamp() ); overlays.set_title_prefix( wstring( L"" ) ); output_new_frame(); /* Restore terminal and terminal-driver state */ swrite( STDOUT_FILENO, display.close().c_str() ); if ( tcsetattr( STDIN_FILENO, TCSANOW, &saved_termios ) < 0 ) { perror( "tcsetattr" ); exit( 1 ); } if ( still_connecting() ) { fprintf( stderr, "\nmosh did not make a successful connection to %s:%s.\n", ip.c_str(), port.c_str() ); fprintf( stderr, "Please verify that UDP port %s is not firewalled and can reach the server.\n\n", port.c_str() ); fprintf( stderr, "(By default, mosh uses a UDP port between 60000 and 61000. The -p option\nselects a specific UDP port number.)\n" ); } else if ( network ) { if ( !clean_shutdown ) { fprintf( stderr, "\n\nmosh did not shut down cleanly. Please note that the\nmosh-server process may still be running on the server.\n" ); } } } void STMClient::main_init( void ) { Select &sel = Select::get_instance(); sel.add_signal( SIGWINCH ); sel.add_signal( SIGTERM ); sel.add_signal( SIGINT ); sel.add_signal( SIGHUP ); sel.add_signal( SIGPIPE ); sel.add_signal( SIGCONT ); /* get initial window size */ if ( ioctl( STDIN_FILENO, TIOCGWINSZ, &window_size ) < 0 ) { perror( "ioctl TIOCGWINSZ" ); return; } /* local state */ local_framebuffer = new Terminal::Framebuffer( window_size.ws_col, window_size.ws_row ); new_state = new Terminal::Framebuffer( 1, 1 ); /* initialize screen */ string init = display.new_frame( false, *local_framebuffer, *local_framebuffer ); swrite( STDOUT_FILENO, init.data(), init.size() ); /* open network */ Network::UserStream blank; Terminal::Complete local_terminal( window_size.ws_col, window_size.ws_row ); network = new Network::Transport< Network::UserStream, Terminal::Complete >( blank, local_terminal, key.c_str(), ip.c_str(), port.c_str() ); network->set_send_delay( 1 ); /* minimal delay on outgoing keystrokes */ /* tell server the size of the terminal */ network->get_current_state().push_back( Parser::Resize( window_size.ws_col, window_size.ws_row ) ); } void STMClient::output_new_frame( void ) { if ( !network ) { /* clean shutdown even when not initialized */ return; } /* fetch target state */ *new_state = network->get_latest_remote_state().state.get_fb(); /* apply local overlays */ overlays.apply( *new_state ); /* apply any mutations */ display.downgrade( *new_state ); /* calculate minimal difference from where we are */ const string diff( display.new_frame( !repaint_requested, *local_framebuffer, *new_state ) ); swrite( STDOUT_FILENO, diff.data(), diff.size() ); repaint_requested = false; /* switch pointers */ Terminal::Framebuffer *tmp = new_state; new_state = local_framebuffer; local_framebuffer = tmp; } void STMClient::process_network_input( void ) { network->recv(); /* Now give hints to the overlays */ overlays.get_notification_engine().server_heard( network->get_latest_remote_state().timestamp ); overlays.get_notification_engine().server_acked( network->get_sent_state_acked_timestamp() ); overlays.get_prediction_engine().set_local_frame_acked( network->get_sent_state_acked() ); overlays.get_prediction_engine().set_send_interval( network->send_interval() ); overlays.get_prediction_engine().set_local_frame_late_acked( network->get_latest_remote_state().state.get_echo_ack() ); } bool STMClient::process_user_input( int fd ) { const int buf_size = 16384; char buf[ buf_size ]; /* fill buffer if possible */ ssize_t bytes_read = read( fd, buf, buf_size ); if ( bytes_read == 0 ) { /* EOF */ return false; } else if ( bytes_read < 0 ) { perror( "read" ); return false; } if ( !network->shutdown_in_progress() ) { overlays.get_prediction_engine().set_local_frame_sent( network->get_sent_state_last() ); for ( int i = 0; i < bytes_read; i++ ) { char the_byte = buf[ i ]; overlays.get_prediction_engine().new_user_byte( the_byte, *local_framebuffer ); if ( quit_sequence_started ) { if ( the_byte == '.' ) { /* Quit sequence is Ctrl-^ . */ if ( network->has_remote_addr() && (!network->shutdown_in_progress()) ) { overlays.get_notification_engine().set_notification_string( wstring( L"Exiting on user request..." ), true ); network->start_shutdown(); return true; } else { return false; } } else if ( the_byte == 0x1a ) { /* Suspend sequence is escape_key Ctrl-Z */ /* Restore terminal and terminal-driver state */ swrite( STDOUT_FILENO, display.close().c_str() ); if ( tcsetattr( STDIN_FILENO, TCSANOW, &saved_termios ) < 0 ) { perror( "tcsetattr" ); exit( 1 ); } printf( "\n\033[37;44m[mosh is suspended.]\033[m\n" ); fflush( NULL ); /* actually suspend */ kill( 0, SIGSTOP ); resume(); } else if ( (the_byte == escape_pass_key) || (the_byte == escape_pass_key2) ) { /* Emulation sequence to type escape_key is escape_key + escape_pass_key (that is escape key without Ctrl) */ network->get_current_state().push_back( Parser::UserByte( escape_key ) ); } else { /* Escape key followed by anything other than . and ^ gets sent literally */ network->get_current_state().push_back( Parser::UserByte( escape_key ) ); network->get_current_state().push_back( Parser::UserByte( the_byte ) ); } quit_sequence_started = false; if ( overlays.get_notification_engine().get_notification_string() == escape_key_help ) { overlays.get_notification_engine().set_notification_string( L"" ); } continue; } quit_sequence_started = (escape_key > 0) && (the_byte == escape_key) && (lf_entered || (! escape_requires_lf)); if ( quit_sequence_started ) { lf_entered = false; overlays.get_notification_engine().set_notification_string( escape_key_help, true, false ); continue; } lf_entered = ( (the_byte == 0x0A) || (the_byte == 0x0D) ); /* LineFeed, Ctrl-J, '\n' or CarriageReturn, Ctrl-M, '\r' */ if ( the_byte == 0x0C ) { /* Ctrl-L */ repaint_requested = true; } network->get_current_state().push_back( Parser::UserByte( the_byte ) ); } } return true; } bool STMClient::process_resize( void ) { /* get new size */ if ( ioctl( STDIN_FILENO, TIOCGWINSZ, &window_size ) < 0 ) { perror( "ioctl TIOCGWINSZ" ); return false; } /* tell remote emulator */ Parser::Resize res( window_size.ws_col, window_size.ws_row ); if ( !network->shutdown_in_progress() ) { network->get_current_state().push_back( res ); } /* note remote emulator will probably reply with its own Resize to adjust our state */ /* tell prediction engine */ overlays.get_prediction_engine().reset(); return true; } void STMClient::main( void ) { /* initialize signal handling and structures */ main_init(); /* prepare to poll for events */ Select &sel = Select::get_instance(); while ( 1 ) { try { output_new_frame(); int wait_time = min( network->wait_time(), overlays.wait_time() ); /* Handle startup "Connecting..." message */ if ( still_connecting() ) { wait_time = min( 250, wait_time ); } /* poll for events */ /* network->fd() can in theory change over time */ sel.clear_fds(); std::vector< int > fd_list( network->fds() ); for ( std::vector< int >::const_iterator it = fd_list.begin(); it != fd_list.end(); it++ ) { sel.add_fd( *it ); } sel.add_fd( STDIN_FILENO ); int active_fds = sel.select( wait_time ); if ( active_fds < 0 ) { perror( "select" ); break; } bool network_ready_to_read = false; for ( std::vector< int >::const_iterator it = fd_list.begin(); it != fd_list.end(); it++ ) { if ( sel.read( *it ) ) { /* packet received from the network */ /* we only read one socket each run */ network_ready_to_read = true; } if ( sel.error( *it ) ) { /* network problem */ break; } } if ( network_ready_to_read ) { process_network_input(); } if ( sel.read( STDIN_FILENO ) ) { /* input from the user needs to be fed to the network */ if ( !process_user_input( STDIN_FILENO ) ) { if ( !network->has_remote_addr() ) { break; } else if ( !network->shutdown_in_progress() ) { overlays.get_notification_engine().set_notification_string( wstring( L"Exiting..." ), true ); network->start_shutdown(); } } } if ( sel.signal( SIGWINCH ) ) { /* resize */ if ( !process_resize() ) { return; } } if ( sel.signal( SIGCONT ) ) { resume(); } if ( sel.signal( SIGTERM ) || sel.signal( SIGINT ) || sel.signal( SIGHUP ) || sel.signal( SIGPIPE ) ) { /* shutdown signal */ if ( !network->has_remote_addr() ) { break; } else if ( !network->shutdown_in_progress() ) { overlays.get_notification_engine().set_notification_string( wstring( L"Signal received, shutting down..." ), true ); network->start_shutdown(); } } if ( sel.error( STDIN_FILENO ) ) { /* user problem */ if ( !network->has_remote_addr() ) { break; } else if ( !network->shutdown_in_progress() ) { overlays.get_notification_engine().set_notification_string( wstring( L"Exiting..." ), true ); network->start_shutdown(); } } /* quit if our shutdown has been acknowledged */ if ( network->shutdown_in_progress() && network->shutdown_acknowledged() ) { clean_shutdown = true; break; } /* quit after shutdown acknowledgement timeout */ if ( network->shutdown_in_progress() && network->shutdown_ack_timed_out() ) { break; } /* quit if we received and acknowledged a shutdown request */ if ( network->counterparty_shutdown_ack_sent() ) { clean_shutdown = true; break; } /* write diagnostic message if can't reach server */ if ( still_connecting() && (!network->shutdown_in_progress()) && (timestamp() - network->get_latest_remote_state().timestamp > 250) ) { if ( timestamp() - network->get_latest_remote_state().timestamp > 15000 ) { if ( !network->shutdown_in_progress() ) { overlays.get_notification_engine().set_notification_string( wstring( L"Timed out waiting for server..." ), true ); network->start_shutdown(); } } else { overlays.get_notification_engine().set_notification_string( connecting_notification ); } } else if ( (network->get_remote_state_num() != 0) && (overlays.get_notification_engine().get_notification_string() == connecting_notification) ) { overlays.get_notification_engine().set_notification_string( L"" ); } network->tick(); const Network::NetworkException *exn = network->get_send_exception(); if ( exn ) { overlays.get_notification_engine().set_network_exception( *exn ); } else { overlays.get_notification_engine().clear_network_exception(); } } catch ( const Network::NetworkException &e ) { if ( !network->shutdown_in_progress() ) { overlays.get_notification_engine().set_network_exception( e ); } struct timespec req; req.tv_sec = 0; req.tv_nsec = 200000000; /* 0.2 sec */ nanosleep( &req, NULL ); freeze_timestamp(); } catch ( const Crypto::CryptoException &e ) { if ( e.fatal ) { throw; } else { wchar_t tmp[ 128 ]; swprintf( tmp, 128, L"Crypto exception: %s", e.what() ); overlays.get_notification_engine().set_notification_string( wstring( tmp ) ); } } } } mosh-1.2.5/src/frontend/stmclient.h000066400000000000000000000106221255051512600172510ustar00rootroot00000000000000/* Mosh: the mobile shell Copyright 2012 Keith Winstein This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . In addition, as a special exception, the copyright holders give permission to link the code of portions of this program with the OpenSSL library under certain conditions as described in each individual source file, and distribute linked combinations including the two. You must obey the GNU General Public License in all respects for all of the code used other than OpenSSL. If you modify file(s) with this exception, you may extend this exception to your version of the file(s), but you are not obligated to do so. If you do not wish to do so, delete this exception statement from your version. If you delete this exception statement from all source files in the program, then also delete it here. */ #ifndef STM_CLIENT_HPP #define STM_CLIENT_HPP #include #include #include #include "completeterminal.h" #include "networktransport.h" #include "user.h" #include "terminaloverlay.h" class STMClient { private: std::string ip; std::string port; std::string key; int escape_key; int escape_pass_key; int escape_pass_key2; bool escape_requires_lf; std::wstring escape_key_help; struct termios saved_termios, raw_termios; struct winsize window_size; Terminal::Framebuffer *local_framebuffer, *new_state; Overlay::OverlayManager overlays; Network::Transport< Network::UserStream, Terminal::Complete > *network; Terminal::Display display; std::wstring connecting_notification; bool repaint_requested, lf_entered, quit_sequence_started; bool clean_shutdown; void main_init( void ); void process_network_input( void ); bool process_user_input( int fd ); bool process_resize( void ); void output_new_frame( void ); bool still_connecting( void ) const { /* Initially, network == NULL */ return network && ( network->get_remote_state_num() == 0 ); } void resume( void ); /* restore state after SIGCONT */ public: STMClient( const char *s_ip, const char *s_port, const char *s_key, const char *predict_mode ) : ip( s_ip ), port( s_port ), key( s_key ), escape_key( 0x1E ), escape_pass_key( '^' ), escape_pass_key2( '^' ), escape_requires_lf( false ), escape_key_help( L"?" ), saved_termios(), raw_termios(), window_size(), local_framebuffer( NULL ), new_state( NULL ), overlays(), network( NULL ), display( true ), /* use TERM environment var to initialize display */ connecting_notification(), repaint_requested( false ), lf_entered( false ), quit_sequence_started( false ), clean_shutdown( false ) { if ( predict_mode ) { if ( !strcmp( predict_mode, "always" ) ) { overlays.get_prediction_engine().set_display_preference( Overlay::PredictionEngine::Always ); } else if ( !strcmp( predict_mode, "never" ) ) { overlays.get_prediction_engine().set_display_preference( Overlay::PredictionEngine::Never ); } else if ( !strcmp( predict_mode, "adaptive" ) ) { overlays.get_prediction_engine().set_display_preference( Overlay::PredictionEngine::Adaptive ); } else if ( !strcmp( predict_mode, "experimental" ) ) { overlays.get_prediction_engine().set_display_preference( Overlay::PredictionEngine::Experimental ); } else { fprintf( stderr, "Unknown prediction mode %s.\n", predict_mode ); exit( 1 ); } } } void init( void ); void shutdown( void ); void main( void ); ~STMClient() { if ( local_framebuffer != NULL ) { delete local_framebuffer; } if ( new_state != NULL ) { delete new_state; } if ( network != NULL ) { delete network; } } /* unused */ STMClient( const STMClient & ); STMClient & operator=( const STMClient & ); }; #endif mosh-1.2.5/src/frontend/terminaloverlay.cc000066400000000000000000000622711255051512600206310ustar00rootroot00000000000000/* Mosh: the mobile shell Copyright 2012 Keith Winstein This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . In addition, as a special exception, the copyright holders give permission to link the code of portions of this program with the OpenSSL library under certain conditions as described in each individual source file, and distribute linked combinations including the two. You must obey the GNU General Public License in all respects for all of the code used other than OpenSSL. If you modify file(s) with this exception, you may extend this exception to your version of the file(s), but you are not obligated to do so. If you do not wish to do so, delete this exception statement from your version. If you delete this exception statement from all source files in the program, then also delete it here. */ #include #include #include #include #include #include "terminaloverlay.h" using namespace Overlay; using std::max; using std::mem_fun_ref; using std::bind2nd; void ConditionalOverlayCell::apply( Framebuffer &fb, uint64_t confirmed_epoch, int row, bool flag ) const { if ( (!active) || (row >= fb.ds.get_height()) || (col >= fb.ds.get_width()) ) { return; } if ( tentative( confirmed_epoch ) ) { return; } if ( replacement.is_blank() && fb.get_cell( row, col )->is_blank() ) { flag = false; } if ( unknown ) { if ( flag && ( col != fb.ds.get_width() - 1 ) ) { fb.get_mutable_cell( row, col )->renditions.underlined = true; } return; } if ( !(*(fb.get_cell( row, col )) == replacement) ) { *(fb.get_mutable_cell( row, col )) = replacement; if ( flag ) { fb.get_mutable_cell( row, col )->renditions.underlined = true; } } } Validity ConditionalOverlayCell::get_validity( const Framebuffer &fb, int row, uint64_t early_ack __attribute__((unused)), uint64_t late_ack ) const { if ( !active ) { return Inactive; } if ( (row >= fb.ds.get_height()) || (col >= fb.ds.get_width()) ) { return IncorrectOrExpired; } const Cell ¤t = *( fb.get_cell( row, col ) ); /* see if it hasn't been updated yet */ if ( late_ack >= expiration_frame ) { if ( unknown ) { return CorrectNoCredit; } if ( replacement.is_blank() ) { /* too easy for this to trigger falsely */ return CorrectNoCredit; } if ( current.contents_match( replacement ) ) { vector::const_iterator it = original_contents.begin(); for ( ; it != original_contents.end(); it++ ) { if ( it->contents_match( replacement ) ) break; } if ( it == original_contents.end() ) { return Correct; } else { return CorrectNoCredit; } } else { return IncorrectOrExpired; } } return Pending; } Validity ConditionalCursorMove::get_validity( const Framebuffer &fb, uint64_t early_ack __attribute((unused)), uint64_t late_ack ) const { if ( !active ) { return Inactive; } if ( (row >= fb.ds.get_height()) || (col >= fb.ds.get_width()) ) { // assert( false ); // fprintf( stderr, "Crazy cursor (%d,%d)!\n", row, col ); return IncorrectOrExpired; } if ( late_ack >= expiration_frame ) { if ( (fb.ds.get_cursor_col() == col) && (fb.ds.get_cursor_row() == row) ) { return Correct; } else { return IncorrectOrExpired; } } return Pending; } void ConditionalCursorMove::apply( Framebuffer &fb, uint64_t confirmed_epoch ) const { if ( !active ) { return; } if ( tentative( confirmed_epoch ) ) { return; } assert( row < fb.ds.get_height() ); assert( col < fb.ds.get_width() ); assert( !fb.ds.origin_mode ); fb.ds.move_row( row, false ); fb.ds.move_col( col, false, false ); } NotificationEngine::NotificationEngine() : last_word_from_server( timestamp() ), last_acked_state( timestamp() ), escape_key_string(), message(), message_is_network_exception( false ), message_expiration( -1 ), show_quit_keystroke( true ) {} static std::string human_readable_duration( int num_seconds, const std::string &seconds_abbr ) { char tmp[ 128 ]; if ( num_seconds < 60 ) { snprintf( tmp, 128, "%d %s", num_seconds, seconds_abbr.c_str() ); } else if ( num_seconds < 3600 ) { snprintf( tmp, 128, "%d:%02d", num_seconds / 60, num_seconds % 60 ); } else { snprintf( tmp, 128, "%d:%02d:%02d", num_seconds / 3600, (num_seconds / 60) % 60, num_seconds % 60 ); } return tmp; } void NotificationEngine::apply( Framebuffer &fb ) const { uint64_t now = timestamp(); bool time_expired = need_countup( now ); if ( message.empty() && !time_expired ) { return; } assert( fb.ds.get_width() > 0 ); assert( fb.ds.get_height() > 0 ); /* hide cursor if necessary */ if ( fb.ds.get_cursor_row() == 0 ) { fb.ds.cursor_visible = false; } /* draw bar across top of screen */ Cell notification_bar( 0 ); notification_bar.renditions.foreground_color = 37; notification_bar.renditions.background_color = 44; notification_bar.contents.push_back( 0x20 ); for ( int i = 0; i < fb.ds.get_width(); i++ ) { *(fb.get_mutable_cell( 0, i )) = notification_bar; } /* write message */ wchar_t tmp[ 128 ]; /* We want to prefer the "last contact" message if we simply haven't heard from the server in a while, but print the "last reply" message if the problem is uplink-only. */ double since_heard = (double)(now - last_word_from_server) / 1000.0; double since_ack = (double)(now - last_acked_state) / 1000.0; const char server_message[] = "contact"; const char reply_message[] = "reply"; double time_elapsed = since_heard; const char *explanation = server_message; if ( reply_late( now ) && (!server_late( now )) ) { time_elapsed = since_ack; explanation = reply_message; } const static char blank[] = ""; const char *keystroke_str = show_quit_keystroke ? escape_key_string.c_str() : blank; if ( message.empty() && (!time_expired) ) { return; } else if ( message.empty() && time_expired ) { swprintf( tmp, 128, L"mosh: Last %s %s ago.%s", explanation, human_readable_duration( static_cast( time_elapsed ), "seconds" ).c_str(), keystroke_str ); } else if ( (!message.empty()) && (!time_expired) ) { swprintf( tmp, 128, L"mosh: %ls%s", message.c_str(), keystroke_str ); } else { swprintf( tmp, 128, L"mosh: %ls (%s without %s.)%s", message.c_str(), human_readable_duration( static_cast( time_elapsed ), "s" ).c_str(), explanation, keystroke_str ); } wstring string_to_draw( tmp ); int overlay_col = 0; Cell *combining_cell = fb.get_mutable_cell( 0, 0 ); /* We unfortunately duplicate the terminal's logic for how to render a Unicode sequence into graphemes */ for ( wstring::const_iterator i = string_to_draw.begin(); i != string_to_draw.end(); i++ ) { if ( overlay_col >= fb.ds.get_width() ) { break; } wchar_t ch = *i; int chwidth = ch == L'\0' ? -1 : wcwidth( ch ); Cell *this_cell = 0; switch ( chwidth ) { case 1: /* normal character */ case 2: /* wide character */ this_cell = fb.get_mutable_cell( 0, overlay_col ); fb.reset_cell( this_cell ); this_cell->renditions.bold = true; this_cell->renditions.foreground_color = 37; this_cell->renditions.background_color = 44; this_cell->contents.push_back( ch ); this_cell->width = chwidth; combining_cell = this_cell; overlay_col += chwidth; break; case 0: /* combining character */ if ( !combining_cell ) { break; } if ( combining_cell->contents.size() == 0 ) { assert( combining_cell->width == 1 ); combining_cell->fallback = true; overlay_col++; } if ( combining_cell->contents.size() < 16 ) { combining_cell->contents.push_back( ch ); } break; case -1: /* unprintable character */ break; default: assert( false ); } } } void NotificationEngine::adjust_message( void ) { if ( timestamp() >= message_expiration ) { message.clear(); } } int NotificationEngine::wait_time( void ) const { uint64_t next_expiry = INT_MAX; uint64_t now = timestamp(); next_expiry = std::min( next_expiry, message_expiration - now ); if ( need_countup( now ) ) { uint64_t countup_interval = 1000; if ( ( now - last_word_from_server ) > 60000 ) { /* If we've been disconnected for 60 seconds, save power by updating the display less often. See #243. */ countup_interval = Network::ACK_INTERVAL; } next_expiry = std::min( next_expiry, countup_interval ); } return next_expiry; } void OverlayManager::apply( Framebuffer &fb ) { predictions.cull( fb ); predictions.apply( fb ); notifications.adjust_message(); notifications.apply( fb ); title.apply( fb ); } void TitleEngine::set_prefix( const wstring s ) { prefix = deque( s.begin(), s.end() ); } void ConditionalOverlayRow::apply( Framebuffer &fb, uint64_t confirmed_epoch, bool flag ) const { for ( overlay_cells_type::const_iterator it = overlay_cells.begin(); it != overlay_cells.end(); it++ ) { it->apply( fb, confirmed_epoch, row_num, flag ); } } void PredictionEngine::apply( Framebuffer &fb ) const { bool show = (display_preference != Never) && ( srtt_trigger || glitch_trigger || (display_preference == Always) || (display_preference == Experimental) ); if ( show ) { for ( cursors_type::const_iterator it = cursors.begin(); it != cursors.end(); it++ ) { it->apply( fb, confirmed_epoch ); } for ( overlays_type::const_iterator it = overlays.begin(); it != overlays.end(); it++ ) { it->apply( fb, confirmed_epoch, flagging ); } } } void PredictionEngine::kill_epoch( uint64_t epoch, const Framebuffer &fb ) { cursors.remove_if( bind2nd( mem_fun_ref( &ConditionalCursorMove::tentative ), epoch - 1 ) ); cursors.push_back( ConditionalCursorMove( local_frame_sent + 1, fb.ds.get_cursor_row(), fb.ds.get_cursor_col(), prediction_epoch ) ); cursor().active = true; for ( overlays_type::iterator i = overlays.begin(); i != overlays.end(); i++ ) { for ( overlay_cells_type::iterator j = i->overlay_cells.begin(); j != i->overlay_cells.end(); j++ ) { if ( j->tentative( epoch - 1 ) ) { j->reset(); } } } become_tentative(); } void PredictionEngine::reset( void ) { cursors.clear(); overlays.clear(); become_tentative(); // fprintf( stderr, "RESETTING\n" ); } void PredictionEngine::init_cursor( const Framebuffer &fb ) { if ( cursors.empty() ) { /* initialize new cursor prediction */ cursors.push_back( ConditionalCursorMove( local_frame_sent + 1, fb.ds.get_cursor_row(), fb.ds.get_cursor_col(), prediction_epoch ) ); cursor().active = true; } else if ( cursor().tentative_until_epoch != prediction_epoch ) { cursors.push_back( ConditionalCursorMove( local_frame_sent + 1, cursor().row, cursor().col, prediction_epoch ) ); cursor().active = true; } } void PredictionEngine::cull( const Framebuffer &fb ) { if ( display_preference == Never ) { return; } if ( (last_height != fb.ds.get_height()) || (last_width != fb.ds.get_width()) ) { last_height = fb.ds.get_height(); last_width = fb.ds.get_width(); reset(); } uint64_t now = timestamp(); /* control srtt_trigger with hysteresis */ if ( send_interval > SRTT_TRIGGER_HIGH ) { srtt_trigger = true; } else if ( srtt_trigger && (send_interval <= SRTT_TRIGGER_LOW) /* 20 ms is current minimum value */ && (!active()) ) { /* only turn off when no predictions being shown */ srtt_trigger = false; } /* control underlining with hysteresis */ if ( send_interval > FLAG_TRIGGER_HIGH ) { flagging = true; } else if ( send_interval <= FLAG_TRIGGER_LOW ) { flagging = false; } /* really big glitches also activate underlining */ if ( glitch_trigger > GLITCH_REPAIR_COUNT ) { flagging = true; } /* go through cell predictions */ overlays_type::iterator i = overlays.begin(); while ( i != overlays.end() ) { overlays_type::iterator inext = i; inext++; if ( (i->row_num < 0) || (i->row_num >= fb.ds.get_height()) ) { overlays.erase( i ); i = inext; continue; } for ( overlay_cells_type::iterator j = i->overlay_cells.begin(); j != i->overlay_cells.end(); j++ ) { switch ( j->get_validity( fb, i->row_num, local_frame_acked, local_frame_late_acked ) ) { case IncorrectOrExpired: if ( j->tentative( confirmed_epoch ) ) { /* fprintf( stderr, "Bad tentative prediction in row %d, col %d (think %lc, actually %lc)\n", i->row_num, j->col, j->replacement.debug_contents(), fb.get_cell( i->row_num, j->col )->debug_contents() ); */ if ( display_preference == Experimental ) { j->reset(); } else { kill_epoch( j->tentative_until_epoch, fb ); } /* if ( j->display_time != uint64_t(-1) ) { fprintf( stderr, "TIMING %ld - %ld (TENT)\n", time(NULL), now - j->display_time ); } */ } else { /* fprintf( stderr, "[%d=>%d] Killing prediction in row %d, col %d (think %lc, actually %lc)\n", (int)local_frame_acked, (int)j->expiration_frame, i->row_num, j->col, j->replacement.debug_contents(), fb.get_cell( i->row_num, j->col )->debug_contents() ); */ /* if ( j->display_time != uint64_t(-1) ) { fprintf( stderr, "TIMING %ld - %ld\n", time(NULL), now - j->display_time ); } */ if ( display_preference == Experimental ) { j->reset(); } else { reset(); return; } } break; case Correct: /* if ( j->display_time != uint64_t(-1) ) { fprintf( stderr, "TIMING %ld + %ld\n", now, now - j->display_time ); } */ if ( j->tentative_until_epoch > confirmed_epoch ) { confirmed_epoch = j->tentative_until_epoch; /* fprintf( stderr, "%lc in (%d,%d) confirms epoch %lu (predicting in epoch %lu)\n", j->replacement.debug_contents(), i->row_num, j->col, confirmed_epoch, prediction_epoch ); */ } /* When predictions come in quickly, slowly take away the glitch trigger. */ if ( (now - j->prediction_time) < GLITCH_THRESHOLD ) { if ( (glitch_trigger > 0) && (now - GLITCH_REPAIR_MININTERVAL >= last_quick_confirmation) ) { glitch_trigger--; last_quick_confirmation = now; } } /* match rest of row to the actual renditions */ { const Renditions &actual_renditions = fb.get_cell( i->row_num, j->col )->renditions; for ( overlay_cells_type::iterator k = j; k != i->overlay_cells.end(); k++ ) { k->replacement.renditions = actual_renditions; } } /* no break */ case CorrectNoCredit: j->reset(); break; case Pending: /* When a prediction takes a long time to be confirmed, we activate the predictions even if SRTT is low */ if ( (now - j->prediction_time) >= GLITCH_FLAG_THRESHOLD ) { glitch_trigger = GLITCH_REPAIR_COUNT * 2; /* display and underline */ } else if ( ((now - j->prediction_time) >= GLITCH_THRESHOLD) && (glitch_trigger < GLITCH_REPAIR_COUNT) ) { glitch_trigger = GLITCH_REPAIR_COUNT; /* just display */ } break; default: break; } } i = inext; } /* go through cursor predictions */ if ( !cursors.empty() ) { if ( cursor().get_validity( fb, local_frame_acked, local_frame_late_acked ) == IncorrectOrExpired ) { /* fprintf( stderr, "Sadly, we're predicting (%d,%d) vs. (%d,%d) [tau: %ld, expiration_time=%ld, now=%ld]\n", cursor().row, cursor().col, fb.ds.get_cursor_row(), fb.ds.get_cursor_col(), cursor().tentative_until_epoch, cursor().expiration_time, now ); */ if ( display_preference == Experimental ) { cursors.clear(); } else { reset(); return; } } } /* NB: switching from list to another STL container could break this code. So we don't use the cursors_type typedef. */ for ( list::iterator it = cursors.begin(); it != cursors.end(); ) { if ( it->get_validity( fb, local_frame_acked, local_frame_late_acked ) != Pending ) { it = cursors.erase( it ); } else { it++; } } } ConditionalOverlayRow & PredictionEngine::get_or_make_row( int row_num, int num_cols ) { overlays_type::iterator it = find_if( overlays.begin(), overlays.end(), bind2nd( mem_fun_ref( &ConditionalOverlayRow::row_num_eq ), row_num ) ); if ( it != overlays.end() ) { return *it; } else { /* make row */ ConditionalOverlayRow r( row_num ); r.overlay_cells.reserve( num_cols ); for ( int i = 0; i < num_cols; i++ ) { r.overlay_cells.push_back( ConditionalOverlayCell( 0, i, prediction_epoch ) ); assert( r.overlay_cells[ i ].col == i ); } overlays.push_back( r ); return overlays.back(); } } void PredictionEngine::new_user_byte( char the_byte, const Framebuffer &fb ) { if ( display_preference == Never ) { return; } else if ( display_preference == Experimental ) { prediction_epoch = confirmed_epoch; } cull( fb ); uint64_t now = timestamp(); /* translate application-mode cursor control function to ANSI cursor control sequence */ if ( (last_byte == 0x1b) && (the_byte == 'O') ) { the_byte = '['; } last_byte = the_byte; list actions( parser.input( the_byte ) ); for ( list::iterator it = actions.begin(); it != actions.end(); it++ ) { Parser::Action *act = *it; /* fprintf( stderr, "Action: %s (%lc)\n", act->name().c_str(), act->char_present ? act->ch : L'_' ); */ if ( typeid( *act ) == typeid( Parser::Print ) ) { /* make new prediction */ init_cursor( fb ); assert( act->char_present ); wchar_t ch = act->ch; /* XXX handle wide characters */ if ( ch == 0x7f ) { /* backspace */ // fprintf( stderr, "Backspace.\n" ); ConditionalOverlayRow &the_row = get_or_make_row( cursor().row, fb.ds.get_width() ); if ( cursor().col > 0 ) { cursor().col--; cursor().expire( local_frame_sent + 1, now ); for ( int i = cursor().col; i < fb.ds.get_width(); i++ ) { ConditionalOverlayCell &cell = the_row.overlay_cells[ i ]; cell.reset_with_orig(); cell.active = true; cell.tentative_until_epoch = prediction_epoch; cell.expire( local_frame_sent + 1, now ); cell.original_contents.push_back( *fb.get_cell( cursor().row, i ) ); if ( i + 2 < fb.ds.get_width() ) { ConditionalOverlayCell &next_cell = the_row.overlay_cells[ i + 1 ]; const Cell *next_cell_actual = fb.get_cell( cursor().row, i + 1 ); if ( next_cell.active ) { if ( next_cell.unknown ) { cell.unknown = true; } else { cell.unknown = false; cell.replacement = next_cell.replacement; } } else { cell.unknown = false; cell.replacement = *next_cell_actual; } } else { cell.unknown = true; } } } } else if ( (ch < 0x20) || (wcwidth( ch ) != 1) ) { /* unknown print */ become_tentative(); // fprintf( stderr, "Unknown print 0x%x\n", ch ); } else { assert( cursor().row >= 0 ); assert( cursor().col >= 0 ); assert( cursor().row < fb.ds.get_height() ); assert( cursor().col < fb.ds.get_width() ); ConditionalOverlayRow &the_row = get_or_make_row( cursor().row, fb.ds.get_width() ); if ( cursor().col + 1 >= fb.ds.get_width() ) { /* prediction in the last column is tricky */ /* e.g., emacs will show wrap character, shell will just put the character there */ become_tentative(); } /* do the insert */ for ( int i = fb.ds.get_width() - 1; i > cursor().col; i-- ) { ConditionalOverlayCell &cell = the_row.overlay_cells[ i ]; cell.reset_with_orig(); cell.active = true; cell.tentative_until_epoch = prediction_epoch; cell.expire( local_frame_sent + 1, now ); cell.original_contents.push_back( *fb.get_cell( cursor().row, i ) ); ConditionalOverlayCell &prev_cell = the_row.overlay_cells[ i - 1 ]; const Cell *prev_cell_actual = fb.get_cell( cursor().row, i - 1 ); if ( i == fb.ds.get_width() - 1 ) { cell.unknown = true; } else if ( prev_cell.active ) { if ( prev_cell.unknown ) { cell.unknown = true; } else { cell.unknown = false; cell.replacement = prev_cell.replacement; } } else { cell.unknown = false; cell.replacement = *prev_cell_actual; } } ConditionalOverlayCell &cell = the_row.overlay_cells[ cursor().col ]; cell.reset_with_orig(); cell.active = true; cell.tentative_until_epoch = prediction_epoch; cell.expire( local_frame_sent + 1, now ); cell.replacement.renditions = fb.ds.get_renditions(); /* heuristic: match renditions of character to the left */ if ( cursor().col > 0 ) { ConditionalOverlayCell &prev_cell = the_row.overlay_cells[ cursor().col - 1 ]; const Cell *prev_cell_actual = fb.get_cell( cursor().row, cursor().col - 1 ); if ( prev_cell.active && (!prev_cell.unknown) ) { cell.replacement.renditions = prev_cell.replacement.renditions; } else { cell.replacement.renditions = prev_cell_actual->renditions; } } cell.replacement.contents.clear(); cell.replacement.contents.push_back( ch ); cell.original_contents.push_back( *fb.get_cell( cursor().row, cursor().col ) ); /* fprintf( stderr, "[%d=>%d] Predicting %lc in row %d, col %d [tue: %lu]\n", (int)local_frame_acked, (int)cell.expiration_frame, ch, cursor().row, cursor().col, cell.tentative_until_epoch ); */ cursor().expire( local_frame_sent + 1, now ); /* do we need to wrap? */ if ( cursor().col < fb.ds.get_width() - 1 ) { cursor().col++; } else { become_tentative(); newline_carriage_return( fb ); } } } else if ( typeid( *act ) == typeid( Parser::Execute ) ) { if ( act->char_present && (act->ch == 0x0d) /* CR */ ) { become_tentative(); newline_carriage_return( fb ); } else { // fprintf( stderr, "Execute 0x%x\n", act->ch ); become_tentative(); } } else if ( typeid( *act ) == typeid( Parser::Esc_Dispatch ) ) { // fprintf( stderr, "Escape sequence\n" ); become_tentative(); } else if ( typeid( *act ) == typeid( Parser::CSI_Dispatch ) ) { if ( act->char_present && (act->ch == L'C') ) { /* right arrow */ init_cursor( fb ); if ( cursor().col < fb.ds.get_width() - 1 ) { cursor().col++; cursor().expire( local_frame_sent + 1, now ); } } else if ( act->char_present && (act->ch == L'D') ) { /* left arrow */ init_cursor( fb ); if ( cursor().col > 0 ) { cursor().col--; cursor().expire( local_frame_sent + 1, now ); } } else { // fprintf( stderr, "CSI sequence %lc\n", act->ch ); become_tentative(); } } else if ( typeid( *act ) == typeid( Parser::Clear ) ) { } delete act; } } void PredictionEngine::newline_carriage_return( const Framebuffer &fb ) { uint64_t now = timestamp(); init_cursor( fb ); cursor().col = 0; if ( cursor().row == fb.ds.get_height() - 1 ) { /* Don't try to predict scroll until we have versioned cell predictions */ /* for ( overlays_type::iterator i = overlays.begin(); i != overlays.end(); i++ ) { i->row_num--; for ( overlay_cells_type::iterator j = i->overlay_cells.begin(); j != i->overlay_cells.end(); j++ ) { if ( j->active ) { j->expire( local_frame_sent + 1, now ); } } } */ /* make blank prediction for last row */ ConditionalOverlayRow &the_row = get_or_make_row( cursor().row, fb.ds.get_width() ); for ( overlay_cells_type::iterator j = the_row.overlay_cells.begin(); j != the_row.overlay_cells.end(); j++ ) { j->active = true; j->tentative_until_epoch = prediction_epoch; j->expire( local_frame_sent + 1, now ); j->replacement.contents.clear(); } } else { cursor().row++; } } void PredictionEngine::become_tentative( void ) { if ( display_preference != Experimental ) { prediction_epoch++; } /* fprintf( stderr, "Now tentative in epoch %lu (confirmed=%lu)\n", prediction_epoch, confirmed_epoch ); */ } bool PredictionEngine::active( void ) const { if ( !cursors.empty() ) { return true; } for ( overlays_type::const_iterator i = overlays.begin(); i != overlays.end(); i++ ) { for ( overlay_cells_type::const_iterator j = i->overlay_cells.begin(); j != i->overlay_cells.end(); j++ ) { if ( j->active ) { return true; } } } return false; } mosh-1.2.5/src/frontend/terminaloverlay.h000066400000000000000000000253221255051512600204670ustar00rootroot00000000000000/* Mosh: the mobile shell Copyright 2012 Keith Winstein This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . In addition, as a special exception, the copyright holders give permission to link the code of portions of this program with the OpenSSL library under certain conditions as described in each individual source file, and distribute linked combinations including the two. You must obey the GNU General Public License in all respects for all of the code used other than OpenSSL. If you modify file(s) with this exception, you may extend this exception to your version of the file(s), but you are not obligated to do so. If you do not wish to do so, delete this exception statement from your version. If you delete this exception statement from all source files in the program, then also delete it here. */ #ifndef TERMINAL_OVERLAY_HPP #define TERMINAL_OVERLAY_HPP #include "terminalframebuffer.h" #include "network.h" #include "transportsender.h" #include "parser.h" #include #include #include namespace Overlay { using namespace Terminal; using namespace Network; using std::deque; using std::list; using std::vector; using std::wstring; enum Validity { Pending, Correct, CorrectNoCredit, IncorrectOrExpired, Inactive }; class ConditionalOverlay { public: uint64_t expiration_frame; int col; bool active; /* represents a prediction at all */ uint64_t tentative_until_epoch; /* when to show */ uint64_t prediction_time; /* used to find long-pending predictions */ ConditionalOverlay( uint64_t s_exp, int s_col, uint64_t s_tentative ) : expiration_frame( s_exp ), col( s_col ), active( false ), tentative_until_epoch( s_tentative ), prediction_time( uint64_t( -1 ) ) {} virtual ~ConditionalOverlay() {} bool tentative( uint64_t confirmed_epoch ) const { return tentative_until_epoch > confirmed_epoch; } void reset( void ) { expiration_frame = tentative_until_epoch = -1; active = false; } void expire( uint64_t s_exp, uint64_t now ) { expiration_frame = s_exp; prediction_time = now; } }; class ConditionalCursorMove : public ConditionalOverlay { public: int row; void apply( Framebuffer &fb, uint64_t confirmed_epoch ) const; Validity get_validity( const Framebuffer &fb, uint64_t early_ack, uint64_t late_ack ) const; ConditionalCursorMove( uint64_t s_exp, int s_row, int s_col, uint64_t s_tentative ) : ConditionalOverlay( s_exp, s_col, s_tentative ), row( s_row ) {} }; class ConditionalOverlayCell : public ConditionalOverlay { public: Cell replacement; bool unknown; vector original_contents; /* we don't give credit for correct predictions that match the original contents */ void apply( Framebuffer &fb, uint64_t confirmed_epoch, int row, bool flag ) const; Validity get_validity( const Framebuffer &fb, int row, uint64_t early_ack, uint64_t late_ack ) const; ConditionalOverlayCell( uint64_t s_exp, int s_col, uint64_t s_tentative ) : ConditionalOverlay( s_exp, s_col, s_tentative ), replacement( 0 ), unknown( false ), original_contents() {} void reset( void ) { unknown = false; original_contents.clear(); ConditionalOverlay::reset(); } void reset_with_orig( void ) { if ( (!active) || unknown ) { reset(); return; } original_contents.push_back( replacement ); ConditionalOverlay::reset(); } }; class ConditionalOverlayRow { public: int row_num; typedef vector overlay_cells_type; overlay_cells_type overlay_cells; void apply( Framebuffer &fb, uint64_t confirmed_epoch, bool flag ) const; /* For use with find_if */ bool row_num_eq( int v ) const { return row_num == v; } ConditionalOverlayRow( int s_row_num ) : row_num( s_row_num ), overlay_cells() {} }; /* the various overlays */ class NotificationEngine { private: uint64_t last_word_from_server; uint64_t last_acked_state; string escape_key_string; wstring message; bool message_is_network_exception; uint64_t message_expiration; bool show_quit_keystroke; bool server_late( uint64_t ts ) const { return (ts - last_word_from_server) > 6500; } bool reply_late( uint64_t ts ) const { return (ts - last_acked_state) > 10000; } bool need_countup( uint64_t ts ) const { return server_late( ts ) || reply_late( ts ); } public: void adjust_message( void ); void apply( Framebuffer &fb ) const; const wstring &get_notification_string( void ) const { return message; } void server_heard( uint64_t s_last_word ) { last_word_from_server = s_last_word; } void server_acked( uint64_t s_last_acked ) { last_acked_state = s_last_acked; } int wait_time( void ) const; void set_notification_string( const wstring &s_message, bool permanent = false, bool s_show_quit_keystroke = true ) { message = s_message; if ( permanent ) { message_expiration = -1; } else { message_expiration = timestamp() + 1000; } message_is_network_exception = false; show_quit_keystroke = s_show_quit_keystroke; } void set_escape_key_string( const string &s_name ) { char tmp[ 128 ]; snprintf( tmp, sizeof tmp, " [To quit: %s .]", s_name.c_str() ); escape_key_string = tmp; } void set_network_exception( const std::exception &e ) { wchar_t tmp[ 128 ]; swprintf( tmp, 128, L"%s", e.what() ); message = tmp; message_is_network_exception = true; message_expiration = timestamp() + Network::ACK_INTERVAL + 100; } void clear_network_exception() { if ( message_is_network_exception ) { message_expiration = std::min( message_expiration, timestamp() + 1000 ); } } NotificationEngine(); }; class PredictionEngine { private: static const uint64_t SRTT_TRIGGER_LOW = 20; /* <= ms cures SRTT trigger to show predictions */ static const uint64_t SRTT_TRIGGER_HIGH = 30; /* > ms starts SRTT trigger */ static const uint64_t FLAG_TRIGGER_LOW = 50; /* <= ms cures flagging */ static const uint64_t FLAG_TRIGGER_HIGH = 80; /* > ms starts flagging */ static const uint64_t GLITCH_THRESHOLD = 250; /* prediction outstanding this long is glitch */ static const uint64_t GLITCH_REPAIR_COUNT = 10; /* non-glitches required to cure glitch trigger */ static const uint64_t GLITCH_REPAIR_MININTERVAL = 150; /* required time in between non-glitches */ static const uint64_t GLITCH_FLAG_THRESHOLD = 5000; /* prediction outstanding this long => underline */ char last_byte; Parser::UTF8Parser parser; typedef list overlays_type; overlays_type overlays; typedef list cursors_type; cursors_type cursors; typedef ConditionalOverlayRow::overlay_cells_type overlay_cells_type; uint64_t local_frame_sent, local_frame_acked, local_frame_late_acked; ConditionalOverlayRow & get_or_make_row( int row_num, int num_cols ); uint64_t prediction_epoch; uint64_t confirmed_epoch; void become_tentative( void ); void newline_carriage_return( const Framebuffer &fb ); bool flagging; /* whether we are underlining predictions */ bool srtt_trigger; /* show predictions because of slow round trip time */ unsigned int glitch_trigger; /* show predictions temporarily because of long-pending prediction */ uint64_t last_quick_confirmation; ConditionalCursorMove & cursor( void ) { assert( !cursors.empty() ); return cursors.back(); } void kill_epoch( uint64_t epoch, const Framebuffer &fb ); void init_cursor( const Framebuffer &fb ); unsigned int send_interval; int last_height, last_width; public: enum DisplayPreference { Always, Never, Adaptive, Experimental }; private: DisplayPreference display_preference; bool active( void ) const; bool timing_tests_necessary( void ) const { /* Are there any timing-based triggers that haven't fired yet? */ return !( glitch_trigger && flagging ); } public: void set_display_preference( DisplayPreference s_pref ) { display_preference = s_pref; } void apply( Framebuffer &fb ) const; void new_user_byte( char the_byte, const Framebuffer &fb ); void cull( const Framebuffer &fb ); void reset( void ); void set_local_frame_sent( uint64_t x ) { local_frame_sent = x; } void set_local_frame_acked( uint64_t x ) { local_frame_acked = x; } void set_local_frame_late_acked( uint64_t x ) { local_frame_late_acked = x; } void set_send_interval( unsigned int x ) { send_interval = x; } int wait_time( void ) const { return ( timing_tests_necessary() && active() ) ? 50 : INT_MAX; } PredictionEngine( void ) : last_byte( 0 ), parser(), overlays(), cursors(), local_frame_sent( 0 ), local_frame_acked( 0 ), local_frame_late_acked( 0 ), prediction_epoch( 1 ), confirmed_epoch( 0 ), flagging( false ), srtt_trigger( false ), glitch_trigger( 0 ), last_quick_confirmation( 0 ), send_interval( 250 ), last_height( 0 ), last_width( 0 ), display_preference( Adaptive ) { } }; class TitleEngine { private: deque prefix; public: void apply( Framebuffer &fb ) const { fb.prefix_window_title( prefix ); } void set_prefix( const wstring s ); TitleEngine() : prefix() {} }; /* the overlay manager */ class OverlayManager { private: NotificationEngine notifications; PredictionEngine predictions; TitleEngine title; public: void apply( Framebuffer &fb ); NotificationEngine & get_notification_engine( void ) { return notifications; } PredictionEngine & get_prediction_engine( void ) { return predictions; } void set_title_prefix( const wstring s ) { title.set_prefix( s ); } OverlayManager() : notifications(), predictions(), title() {} int wait_time( void ) const { return std::min( notifications.wait_time(), predictions.wait_time() ); } }; } #endif mosh-1.2.5/src/network/000077500000000000000000000000001255051512600147475ustar00rootroot00000000000000mosh-1.2.5/src/network/Makefile.am000066400000000000000000000006471255051512600170120ustar00rootroot00000000000000AM_CPPFLAGS = -I$(srcdir)/../util -I$(srcdir)/../crypto -I../protobufs $(protobuf_CFLAGS) AM_CXXFLAGS = $(WARNING_CXXFLAGS) $(PICKY_CXXFLAGS) $(HARDEN_CFLAGS) $(MISC_CXXFLAGS) noinst_LIBRARIES = libmoshnetwork.a libmoshnetwork_a_SOURCES = network.cc network.h networktransport.cc networktransport.h transportfragment.cc transportfragment.h transportsender.cc transportsender.h transportstate.h compressor.cc compressor.h mosh-1.2.5/src/network/compressor.cc000066400000000000000000000044041255051512600174540ustar00rootroot00000000000000/* Mosh: the mobile shell Copyright 2012 Keith Winstein This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . In addition, as a special exception, the copyright holders give permission to link the code of portions of this program with the OpenSSL library under certain conditions as described in each individual source file, and distribute linked combinations including the two. You must obey the GNU General Public License in all respects for all of the code used other than OpenSSL. If you modify file(s) with this exception, you may extend this exception to your version of the file(s), but you are not obligated to do so. If you do not wish to do so, delete this exception statement from your version. If you delete this exception statement from all source files in the program, then also delete it here. */ #include #include "compressor.h" #include "dos_assert.h" using namespace Network; using namespace std; string Compressor::compress_str( const string &input ) { long unsigned int len = BUFFER_SIZE; dos_assert( Z_OK == compress( buffer, &len, reinterpret_cast( input.data() ), input.size() ) ); return string( reinterpret_cast( buffer ), len ); } string Compressor::uncompress_str( const string &input ) { long unsigned int len = BUFFER_SIZE; dos_assert( Z_OK == uncompress( buffer, &len, reinterpret_cast( input.data() ), input.size() ) ); return string( reinterpret_cast( buffer ), len ); } /* construct on first use */ Compressor & Network::get_compressor( void ) { static Compressor the_compressor; return the_compressor; } mosh-1.2.5/src/network/compressor.h000066400000000000000000000041201255051512600173110ustar00rootroot00000000000000/* Mosh: the mobile shell Copyright 2012 Keith Winstein This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . In addition, as a special exception, the copyright holders give permission to link the code of portions of this program with the OpenSSL library under certain conditions as described in each individual source file, and distribute linked combinations including the two. You must obey the GNU General Public License in all respects for all of the code used other than OpenSSL. If you modify file(s) with this exception, you may extend this exception to your version of the file(s), but you are not obligated to do so. If you do not wish to do so, delete this exception statement from your version. If you delete this exception statement from all source files in the program, then also delete it here. */ #ifndef COMPRESSOR_H #define COMPRESSOR_H #include namespace Network { class Compressor { private: static const int BUFFER_SIZE = 2048 * 2048; /* effective limit on terminal size */ unsigned char *buffer; public: Compressor() : buffer( NULL ) { buffer = new unsigned char[ BUFFER_SIZE ]; } ~Compressor() { if ( buffer ) { delete[] buffer; } } std::string compress_str( const std::string &input ); std::string uncompress_str( const std::string &input ); /* unused */ Compressor( const Compressor & ); Compressor & operator=( const Compressor & ); }; Compressor & get_compressor( void ); } #endif mosh-1.2.5/src/network/network.cc000066400000000000000000000461761255051512600167650ustar00rootroot00000000000000/* Mosh: the mobile shell Copyright 2012 Keith Winstein This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . In addition, as a special exception, the copyright holders give permission to link the code of portions of this program with the OpenSSL library under certain conditions as described in each individual source file, and distribute linked combinations including the two. You must obey the GNU General Public License in all respects for all of the code used other than OpenSSL. If you modify file(s) with this exception, you may extend this exception to your version of the file(s), but you are not obligated to do so. If you do not wish to do so, delete this exception statement from your version. If you delete this exception statement from all source files in the program, then also delete it here. */ #include "config.h" #include #include #ifdef HAVE_SYS_UIO_H #include #endif #include #include #include #include #include #include "dos_assert.h" #include "fatal_assert.h" #include "byteorder.h" #include "network.h" #include "crypto.h" #include "timestamp.h" #ifndef MSG_DONTWAIT #define MSG_DONTWAIT MSG_NONBLOCK #endif #ifndef AI_NUMERICSERV #define AI_NUMERICSERV 0 #endif using namespace std; using namespace Network; using namespace Crypto; const uint64_t DIRECTION_MASK = uint64_t(1) << 63; const uint64_t SEQUENCE_MASK = uint64_t(-1) ^ DIRECTION_MASK; /* Read in packet from coded string */ Packet::Packet( string coded_packet, Session *session ) : seq( -1 ), direction( TO_SERVER ), timestamp( -1 ), timestamp_reply( -1 ), payload() { Message message = session->decrypt( coded_packet ); direction = (message.nonce.val() & DIRECTION_MASK) ? TO_CLIENT : TO_SERVER; seq = message.nonce.val() & SEQUENCE_MASK; dos_assert( message.text.size() >= 2 * sizeof( uint16_t ) ); uint16_t *data = (uint16_t *)message.text.data(); timestamp = be16toh( data[ 0 ] ); timestamp_reply = be16toh( data[ 1 ] ); payload = string( message.text.begin() + 2 * sizeof( uint16_t ), message.text.end() ); } /* Output coded string from packet */ string Packet::tostring( Session *session ) { uint64_t direction_seq = (uint64_t( direction == TO_CLIENT ) << 63) | (seq & SEQUENCE_MASK); uint16_t ts_net[ 2 ] = { static_cast( htobe16( timestamp ) ), static_cast( htobe16( timestamp_reply ) ) }; string timestamps = string( (char *)ts_net, 2 * sizeof( uint16_t ) ); return session->encrypt( Message( Nonce( direction_seq ), timestamps + payload ) ); } Packet Connection::new_packet( string &s_payload ) { uint16_t outgoing_timestamp_reply = -1; uint64_t now = timestamp(); if ( now - saved_timestamp_received_at < 1000 ) { /* we have a recent received timestamp */ /* send "corrected" timestamp advanced by how long we held it */ outgoing_timestamp_reply = saved_timestamp + (now - saved_timestamp_received_at); saved_timestamp = -1; saved_timestamp_received_at = 0; } Packet p( next_seq++, direction, timestamp16(), outgoing_timestamp_reply, s_payload ); return p; } void Connection::hop_port( void ) { assert( !server ); setup(); assert( remote_addr_len != 0 ); socks.push_back( Socket( remote_addr.sa.sa_family ) ); prune_sockets(); } void Connection::prune_sockets( void ) { /* don't keep old sockets if the new socket has been working for long enough */ if ( socks.size() > 1 ) { if ( timestamp() - last_port_choice > MAX_OLD_SOCKET_AGE ) { int num_to_kill = socks.size() - 1; for ( int i = 0; i < num_to_kill; i++ ) { socks.pop_front(); } } } else { return; } /* make sure we don't have too many receive sockets open */ if ( socks.size() > MAX_PORTS_OPEN ) { int num_to_kill = socks.size() - MAX_PORTS_OPEN; for ( int i = 0; i < num_to_kill; i++ ) { socks.pop_front(); } } } Connection::Socket::Socket( int family ) : _fd( socket( family, SOCK_DGRAM, 0 ) ) { if ( _fd < 0 ) { throw NetworkException( "socket", errno ); } /* Disable path MTU discovery */ #ifdef HAVE_IP_MTU_DISCOVER char flag = IP_PMTUDISC_DONT; socklen_t optlen = sizeof( flag ); if ( setsockopt( _fd, IPPROTO_IP, IP_MTU_DISCOVER, &flag, optlen ) < 0 ) { throw NetworkException( "setsockopt", errno ); } #endif // int dscp = 0x92; /* OS X does not have IPTOS_DSCP_AF42 constant */ int dscp = 0x02; /* ECN-capable transport only */ if ( setsockopt( _fd, IPPROTO_IP, IP_TOS, &dscp, sizeof (dscp)) < 0 ) { // perror( "setsockopt( IP_TOS )" ); } /* request explicit congestion notification on received datagrams */ #ifdef HAVE_IP_RECVTOS int tosflag = true; socklen_t tosoptlen = sizeof( tosflag ); if ( setsockopt( _fd, IPPROTO_IP, IP_RECVTOS, &tosflag, tosoptlen ) < 0 ) { perror( "setsockopt( IP_RECVTOS )" ); } #endif } void Connection::setup( void ) { last_port_choice = timestamp(); } const std::vector< int > Connection::fds( void ) const { std::vector< int > ret; for ( std::deque< Socket >::const_iterator it = socks.begin(); it != socks.end(); it++ ) { ret.push_back( it->fd() ); } return ret; } class AddrInfo { public: struct addrinfo *res; AddrInfo( const char *node, const char *service, const struct addrinfo *hints ) : res( NULL ) { int errcode = getaddrinfo( node, service, hints, &res ); if ( errcode != 0 ) { throw NetworkException( std::string( "Bad IP address (" ) + (node != NULL ? node : "(null)") + "): " + gai_strerror( errcode ), 0 ); } } ~AddrInfo() { freeaddrinfo(res); } private: AddrInfo(const AddrInfo &); AddrInfo &operator=(const AddrInfo &); }; Connection::Connection( const char *desired_ip, const char *desired_port ) /* server */ : socks(), has_remote_addr( false ), remote_addr(), remote_addr_len( 0 ), server( true ), MTU( DEFAULT_SEND_MTU ), key(), session( key ), direction( TO_CLIENT ), next_seq( 0 ), saved_timestamp( -1 ), saved_timestamp_received_at( 0 ), expected_receiver_seq( 0 ), last_heard( -1 ), last_port_choice( -1 ), last_roundtrip_success( -1 ), RTT_hit( false ), SRTT( 1000 ), RTTVAR( 500 ), have_send_exception( false ), send_exception() { setup(); /* The mosh wrapper always gives an IP request, in order to deal with multihomed servers. The port is optional. */ /* If an IP request is given, we try to bind to that IP, but we also try INADDR_ANY. If a port request is given, we bind only to that port. */ /* convert port numbers */ int desired_port_low = 0; int desired_port_high = 0; if ( desired_port && !parse_portrange( desired_port, desired_port_low, desired_port_high ) ) { throw NetworkException("Invalid port range", 0); } /* try to bind to desired IP first */ if ( desired_ip ) { try { if ( try_bind( desired_ip, desired_port_low, desired_port_high ) ) { return; } } catch ( const NetworkException &e ) { fprintf( stderr, "Error binding to IP %s: %s\n", desired_ip, e.what() ); } } /* now try any local interface */ try { if ( try_bind( NULL, desired_port_low, desired_port_high ) ) { return; } } catch ( const NetworkException &e ) { fprintf( stderr, "Error binding to any interface: %s\n", e.what() ); throw; /* this time it's fatal */ } assert( false ); throw NetworkException( "Could not bind", errno ); } bool Connection::try_bind( const char *addr, int port_low, int port_high ) { struct addrinfo hints; memset( &hints, 0, sizeof( hints ) ); hints.ai_family = AF_UNSPEC; hints.ai_socktype = SOCK_DGRAM; hints.ai_flags = AI_PASSIVE | AI_NUMERICHOST | AI_NUMERICSERV; AddrInfo ai( addr, "0", &hints ); Addr local_addr; socklen_t local_addr_len = ai.res->ai_addrlen; memcpy( &local_addr.sa, ai.res->ai_addr, local_addr_len ); int search_low = PORT_RANGE_LOW, search_high = PORT_RANGE_HIGH; if ( port_low != 0 ) { /* low port preference */ search_low = port_low; } if ( port_high != 0 ) { /* high port preference */ search_high = port_high; } socks.push_back( Socket( local_addr.sa.sa_family ) ); for ( int i = search_low; i <= search_high; i++ ) { switch (local_addr.sa.sa_family) { case AF_INET: local_addr.sin.sin_port = htons( i ); break; case AF_INET6: local_addr.sin6.sin6_port = htons( i ); break; default: throw NetworkException( "Unknown address family", 0 ); } if ( bind( sock(), &local_addr.sa, local_addr_len ) == 0 ) { return true; } else if ( i == search_high ) { /* last port to search */ int saved_errno = errno; socks.pop_back(); char host[ NI_MAXHOST ], serv[ NI_MAXSERV ]; int errcode = getnameinfo( &local_addr.sa, local_addr_len, host, sizeof( host ), serv, sizeof( serv ), NI_DGRAM | NI_NUMERICHOST | NI_NUMERICSERV ); if ( errcode != 0 ) { throw NetworkException( std::string( "bind: getnameinfo: " ) + gai_strerror( errcode ), 0 ); } fprintf( stderr, "Failed binding to %s:%s\n", host, serv ); throw NetworkException( "bind", saved_errno ); } } assert( false ); return false; } Connection::Connection( const char *key_str, const char *ip, const char *port ) /* client */ : socks(), has_remote_addr( false ), remote_addr(), remote_addr_len( 0 ), server( false ), MTU( DEFAULT_SEND_MTU ), key( key_str ), session( key ), direction( TO_SERVER ), next_seq( 0 ), saved_timestamp( -1 ), saved_timestamp_received_at( 0 ), expected_receiver_seq( 0 ), last_heard( -1 ), last_port_choice( -1 ), last_roundtrip_success( -1 ), RTT_hit( false ), SRTT( 1000 ), RTTVAR( 500 ), have_send_exception( false ), send_exception() { setup(); /* associate socket with remote host and port */ struct addrinfo hints; memset( &hints, 0, sizeof( hints ) ); hints.ai_family = AF_UNSPEC; hints.ai_socktype = SOCK_DGRAM; hints.ai_flags = AI_NUMERICHOST | AI_NUMERICSERV; AddrInfo ai( ip, port, &hints ); fatal_assert( ai.res->ai_addrlen <= sizeof( remote_addr ) ); remote_addr_len = ai.res->ai_addrlen; memcpy( &remote_addr.sa, ai.res->ai_addr, remote_addr_len ); has_remote_addr = true; socks.push_back( Socket( remote_addr.sa.sa_family ) ); } void Connection::send( string s ) { if ( !has_remote_addr ) { return; } Packet px = new_packet( s ); string p = px.tostring( &session ); ssize_t bytes_sent = sendto( sock(), p.data(), p.size(), MSG_DONTWAIT, &remote_addr.sa, remote_addr_len ); if ( bytes_sent == static_cast( p.size() ) ) { have_send_exception = false; } else { /* Notify the frontend on sendto() failure, but don't alter control flow. sendto() success is not very meaningful because packets can be lost in flight anyway. */ have_send_exception = true; send_exception = NetworkException( "sendto", errno ); if ( errno == EMSGSIZE ) { MTU = 500; /* payload MTU of last resort */ } } uint64_t now = timestamp(); if ( server ) { if ( now - last_heard > SERVER_ASSOCIATION_TIMEOUT ) { has_remote_addr = false; fprintf( stderr, "Server now detached from client.\n" ); } } else { /* client */ if ( ( now - last_port_choice > PORT_HOP_INTERVAL ) && ( now - last_roundtrip_success > PORT_HOP_INTERVAL ) ) { hop_port(); } } } string Connection::recv( void ) { assert( !socks.empty() ); for ( std::deque< Socket >::const_iterator it = socks.begin(); it != socks.end(); it++ ) { bool islast = (it + 1) == socks.end(); string payload; try { payload = recv_one( it->fd(), !islast ); } catch ( NetworkException & e ) { if ( (e.the_errno == EAGAIN) || (e.the_errno == EWOULDBLOCK) ) { assert( !islast ); continue; } else { throw; } } /* succeeded */ prune_sockets(); return payload; } assert( false ); return ""; } string Connection::recv_one( int sock_to_recv, bool nonblocking ) { /* receive source address, ECN, and payload in msghdr structure */ Addr packet_remote_addr; struct msghdr header; struct iovec msg_iovec; char msg_payload[ Session::RECEIVE_MTU ]; char msg_control[ Session::RECEIVE_MTU ]; /* receive source address */ header.msg_name = &packet_remote_addr.sa; header.msg_namelen = sizeof( packet_remote_addr ); /* receive payload */ msg_iovec.iov_base = msg_payload; msg_iovec.iov_len = Session::RECEIVE_MTU; header.msg_iov = &msg_iovec; header.msg_iovlen = 1; /* receive explicit congestion notification */ header.msg_control = msg_control; header.msg_controllen = Session::RECEIVE_MTU; /* receive flags */ header.msg_flags = 0; ssize_t received_len = recvmsg( sock_to_recv, &header, nonblocking ? MSG_DONTWAIT : 0 ); if ( received_len < 0 ) { throw NetworkException( "recvmsg", errno ); } if ( header.msg_flags & MSG_TRUNC ) { throw NetworkException( "Received oversize datagram", errno ); } /* receive ECN */ bool congestion_experienced = false; struct cmsghdr *ecn_hdr = CMSG_FIRSTHDR( &header ); if ( ecn_hdr && (ecn_hdr->cmsg_level == IPPROTO_IP) && (ecn_hdr->cmsg_type == IP_TOS) ) { /* got one */ uint8_t *ecn_octet_p = (uint8_t *)CMSG_DATA( ecn_hdr ); assert( ecn_octet_p ); if ( (*ecn_octet_p & 0x03) == 0x03 ) { congestion_experienced = true; } } Packet p( string( msg_payload, received_len ), &session ); dos_assert( p.direction == (server ? TO_SERVER : TO_CLIENT) ); /* prevent malicious playback to sender */ if ( p.seq >= expected_receiver_seq ) { /* don't use out-of-order packets for timestamp or targeting */ expected_receiver_seq = p.seq + 1; /* this is security-sensitive because a replay attack could otherwise screw up the timestamp and targeting */ if ( p.timestamp != uint16_t(-1) ) { saved_timestamp = p.timestamp; saved_timestamp_received_at = timestamp(); if ( congestion_experienced ) { /* signal counterparty to slow down */ /* this will gradually slow the counterparty down to the minimum frame rate */ saved_timestamp -= CONGESTION_TIMESTAMP_PENALTY; if ( server ) { fprintf( stderr, "Received explicit congestion notification.\n" ); } } } if ( p.timestamp_reply != uint16_t(-1) ) { uint16_t now = timestamp16(); double R = timestamp_diff( now, p.timestamp_reply ); if ( R < 5000 ) { /* ignore large values, e.g. server was Ctrl-Zed */ if ( !RTT_hit ) { /* first measurement */ SRTT = R; RTTVAR = R / 2; RTT_hit = true; } else { const double alpha = 1.0 / 8.0; const double beta = 1.0 / 4.0; RTTVAR = (1 - beta) * RTTVAR + ( beta * fabs( SRTT - R ) ); SRTT = (1 - alpha) * SRTT + ( alpha * R ); } } } /* auto-adjust to remote host */ has_remote_addr = true; last_heard = timestamp(); if ( server ) { /* only client can roam */ if ( remote_addr_len != header.msg_namelen || memcmp( &remote_addr, &packet_remote_addr, remote_addr_len ) != 0 ) { remote_addr = packet_remote_addr; remote_addr_len = header.msg_namelen; char host[ NI_MAXHOST ], serv[ NI_MAXSERV ]; int errcode = getnameinfo( &remote_addr.sa, remote_addr_len, host, sizeof( host ), serv, sizeof( serv ), NI_DGRAM | NI_NUMERICHOST | NI_NUMERICSERV ); if ( errcode != 0 ) { throw NetworkException( std::string( "recv_one: getnameinfo: " ) + gai_strerror( errcode ), 0 ); } fprintf( stderr, "Server now attached to client at %s:%s\n", host, serv ); } } } return p.payload; /* we do return out-of-order or duplicated packets to caller */ } std::string Connection::port( void ) const { Addr local_addr; socklen_t addrlen = sizeof( local_addr ); if ( getsockname( sock(), &local_addr.sa, &addrlen ) < 0 ) { throw NetworkException( "getsockname", errno ); } char serv[ NI_MAXSERV ]; int errcode = getnameinfo( &local_addr.sa, addrlen, NULL, 0, serv, sizeof( serv ), NI_DGRAM | NI_NUMERICSERV ); if ( errcode != 0 ) { throw NetworkException( std::string( "port: getnameinfo: " ) + gai_strerror( errcode ), 0 ); } return std::string( serv ); } uint64_t Network::timestamp( void ) { return frozen_timestamp(); } uint16_t Network::timestamp16( void ) { uint16_t ts = timestamp() % 65536; if ( ts == uint16_t(-1) ) { ts++; } return ts; } uint16_t Network::timestamp_diff( uint16_t tsnew, uint16_t tsold ) { int diff = tsnew - tsold; if ( diff < 0 ) { diff += 65536; } assert( diff >= 0 ); assert( diff <= 65535 ); return diff; } uint64_t Connection::timeout( void ) const { uint64_t RTO = lrint( ceil( SRTT + 4 * RTTVAR ) ); if ( RTO < MIN_RTO ) { RTO = MIN_RTO; } else if ( RTO > MAX_RTO ) { RTO = MAX_RTO; } return RTO; } Connection::Socket::~Socket() { if ( close( _fd ) < 0 ) { throw NetworkException( "close", errno ); } } Connection::Socket::Socket( const Socket & other ) : _fd( dup( other._fd ) ) { if ( _fd < 0 ) { throw NetworkException( "socket", errno ); } } Connection::Socket & Connection::Socket::operator=( const Socket & other ) { if ( dup2( other._fd, _fd ) < 0 ) { throw NetworkException( "socket", errno ); } return *this; } bool Connection::parse_portrange( const char * desired_port, int & desired_port_low, int & desired_port_high ) { /* parse "port" or "portlow:porthigh" */ desired_port_low = desired_port_high = 0; char *end; long value; /* parse first (only?) port */ errno = 0; value = strtol( desired_port, &end, 10 ); if ( (errno != 0) || (*end != '\0' && *end != ':') ) { fprintf( stderr, "Invalid (low) port number (%s)\n", desired_port ); return false; } if ( (value < 0) || (value > 65535) ) { fprintf( stderr, "(Low) port number %ld outside valid range [0..65535]\n", value ); return false; } desired_port_low = (int)value; if (*end == '\0') { /* not a port range */ desired_port_high = desired_port_low; return true; } /* port range; parse high port */ const char * cp = end + 1; errno = 0; value = strtol( cp, &end, 10 ); if ( (errno != 0) || (*end != '\0') ) { fprintf( stderr, "Invalid high port number (%s)\n", cp ); return false; } if ( (value < 0) || (value > 65535) ) { fprintf( stderr, "High port number %ld outside valid range [0..65535]\n", value ); return false; } desired_port_high = (int)value; if ( desired_port_low > desired_port_high ) { fprintf( stderr, "Low port %d greater than high port %d\n", desired_port_low, desired_port_high ); return false; } return true; } mosh-1.2.5/src/network/network.h000066400000000000000000000137631255051512600166230ustar00rootroot00000000000000/* Mosh: the mobile shell Copyright 2012 Keith Winstein This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . In addition, as a special exception, the copyright holders give permission to link the code of portions of this program with the OpenSSL library under certain conditions as described in each individual source file, and distribute linked combinations including the two. You must obey the GNU General Public License in all respects for all of the code used other than OpenSSL. If you modify file(s) with this exception, you may extend this exception to your version of the file(s), but you are not obligated to do so. If you do not wish to do so, delete this exception statement from your version. If you delete this exception statement from all source files in the program, then also delete it here. */ #ifndef NETWORK_HPP #define NETWORK_HPP #include #include #include #include #include #include #include #include #include #include #include "crypto.h" using namespace Crypto; namespace Network { static const unsigned int MOSH_PROTOCOL_VERSION = 2; /* bumped for echo-ack */ uint64_t timestamp( void ); uint16_t timestamp16( void ); uint16_t timestamp_diff( uint16_t tsnew, uint16_t tsold ); class NetworkException : public std::exception { public: string function; int the_errno; private: string my_what; public: NetworkException( string s_function="", int s_errno=0) : function( s_function ), the_errno( s_errno ), my_what(function + ": " + strerror(the_errno)) {} const char *what() const throw () { return my_what.c_str(); } ~NetworkException() throw () {} }; enum Direction { TO_SERVER = 0, TO_CLIENT = 1 }; class Packet { public: uint64_t seq; Direction direction; uint16_t timestamp, timestamp_reply; string payload; Packet( uint64_t s_seq, Direction s_direction, uint16_t s_timestamp, uint16_t s_timestamp_reply, string s_payload ) : seq( s_seq ), direction( s_direction ), timestamp( s_timestamp ), timestamp_reply( s_timestamp_reply ), payload( s_payload ) {} Packet( string coded_packet, Session *session ); string tostring( Session *session ); }; union Addr { struct sockaddr sa; struct sockaddr_in sin; struct sockaddr_in6 sin6; struct sockaddr_storage ss; }; class Connection { private: static const int DEFAULT_SEND_MTU = 1300; static const uint64_t MIN_RTO = 50; /* ms */ static const uint64_t MAX_RTO = 1000; /* ms */ static const int PORT_RANGE_LOW = 60001; static const int PORT_RANGE_HIGH = 60999; static const unsigned int SERVER_ASSOCIATION_TIMEOUT = 40000; static const unsigned int PORT_HOP_INTERVAL = 10000; static const unsigned int MAX_PORTS_OPEN = 10; static const unsigned int MAX_OLD_SOCKET_AGE = 60000; static const int CONGESTION_TIMESTAMP_PENALTY = 500; /* ms */ bool try_bind( const char *addr, int port_low, int port_high ); class Socket { private: int _fd; public: int fd( void ) const { return _fd; } Socket( int family ); ~Socket(); Socket( const Socket & other ); Socket & operator=( const Socket & other ); }; std::deque< Socket > socks; bool has_remote_addr; Addr remote_addr; socklen_t remote_addr_len; bool server; int MTU; Base64Key key; Session session; void setup( void ); Direction direction; uint64_t next_seq; uint16_t saved_timestamp; uint64_t saved_timestamp_received_at; uint64_t expected_receiver_seq; uint64_t last_heard; uint64_t last_port_choice; uint64_t last_roundtrip_success; /* transport layer needs to tell us this */ bool RTT_hit; double SRTT; double RTTVAR; /* Exception from send(), to be delivered if the frontend asks for it, without altering control flow. */ bool have_send_exception; NetworkException send_exception; Packet new_packet( string &s_payload ); void hop_port( void ); int sock( void ) const { assert( !socks.empty() ); return socks.back().fd(); } void prune_sockets( void ); string recv_one( int sock_to_recv, bool nonblocking ); public: Connection( const char *desired_ip, const char *desired_port ); /* server */ Connection( const char *key_str, const char *ip, const char *port ); /* client */ void send( string s ); string recv( void ); const std::vector< int > fds( void ) const; int get_MTU( void ) const { return MTU; } std::string port( void ) const; string get_key( void ) const { return key.printable_key(); } bool get_has_remote_addr( void ) const { return has_remote_addr; } uint64_t timeout( void ) const; double get_SRTT( void ) const { return SRTT; } const Addr &get_remote_addr( void ) const { return remote_addr; } socklen_t get_remote_addr_len( void ) const { return remote_addr_len; } const NetworkException *get_send_exception( void ) const { return have_send_exception ? &send_exception : NULL; } void set_last_roundtrip_success( uint64_t s_success ) { last_roundtrip_success = s_success; } static bool parse_portrange( const char * desired_port_range, int & desired_port_low, int & desired_port_high ); }; } #endif mosh-1.2.5/src/network/networktransport.cc000066400000000000000000000161651255051512600207350ustar00rootroot00000000000000/* Mosh: the mobile shell Copyright 2012 Keith Winstein This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . In addition, as a special exception, the copyright holders give permission to link the code of portions of this program with the OpenSSL library under certain conditions as described in each individual source file, and distribute linked combinations including the two. You must obey the GNU General Public License in all respects for all of the code used other than OpenSSL. If you modify file(s) with this exception, you may extend this exception to your version of the file(s), but you are not obligated to do so. If you do not wish to do so, delete this exception statement from your version. If you delete this exception statement from all source files in the program, then also delete it here. */ #include #include "networktransport.h" #include "transportsender.cc" using namespace Network; using namespace std; template Transport::Transport( MyState &initial_state, RemoteState &initial_remote, const char *desired_ip, const char *desired_port ) : connection( desired_ip, desired_port ), sender( &connection, initial_state ), received_states( 1, TimestampedState( timestamp(), 0, initial_remote ) ), receiver_quench_timer( 0 ), last_receiver_state( initial_remote ), fragments(), verbose( false ) { /* server */ } template Transport::Transport( MyState &initial_state, RemoteState &initial_remote, const char *key_str, const char *ip, const char *port ) : connection( key_str, ip, port ), sender( &connection, initial_state ), received_states( 1, TimestampedState( timestamp(), 0, initial_remote ) ), receiver_quench_timer( 0 ), last_receiver_state( initial_remote ), fragments(), verbose( false ) { /* client */ } template void Transport::recv( void ) { string s( connection.recv() ); Fragment frag( s ); if ( fragments.add_fragment( frag ) ) { /* complete packet */ Instruction inst = fragments.get_assembly(); if ( inst.protocol_version() != MOSH_PROTOCOL_VERSION ) { throw NetworkException( "mosh protocol version mismatch", 0 ); } sender.process_acknowledgment_through( inst.ack_num() ); /* inform network layer of roundtrip (end-to-end-to-end) connectivity */ connection.set_last_roundtrip_success( sender.get_sent_state_acked_timestamp() ); /* first, make sure we don't already have the new state */ for ( typename list< TimestampedState >::iterator i = received_states.begin(); i != received_states.end(); i++ ) { if ( inst.new_num() == i->num ) { return; } } /* now, make sure we do have the old state */ bool found = 0; typename list< TimestampedState >::iterator reference_state = received_states.begin(); while ( reference_state != received_states.end() ) { if ( inst.old_num() == reference_state->num ) { found = true; break; } reference_state++; } if ( !found ) { // fprintf( stderr, "Ignoring out-of-order packet. Reference state %d has been discarded or hasn't yet been received.\n", int(inst.old_num) ); return; /* this is security-sensitive and part of how we enforce idempotency */ } /* Do not accept state if our queue is full */ /* This is better than dropping states from the middle of the queue (as sender does), because we don't want to ACK a state and then discard it later. */ process_throwaway_until( inst.throwaway_num() ); if ( received_states.size() > 1024 ) { /* limit on state queue */ uint64_t now = timestamp(); if ( now < receiver_quench_timer ) { /* deny letting state grow further */ if ( verbose ) { fprintf( stderr, "[%u] Receiver queue full, discarding %d (malicious sender or long-unidirectional connectivity?)\n", (unsigned int)(timestamp() % 100000), (int)inst.new_num() ); } return; } else { receiver_quench_timer = now + 15000; } } /* apply diff to reference state */ TimestampedState new_state = *reference_state; new_state.timestamp = timestamp(); new_state.num = inst.new_num(); if ( !inst.diff().empty() ) { new_state.state.apply_string( inst.diff() ); } /* Insert new state in sorted place */ for ( typename list< TimestampedState >::iterator i = received_states.begin(); i != received_states.end(); i++ ) { if ( i->num > new_state.num ) { received_states.insert( i, new_state ); if ( verbose ) { fprintf( stderr, "[%u] Received OUT-OF-ORDER state %d [ack %d]\n", (unsigned int)(timestamp() % 100000), (int)new_state.num, (int)inst.ack_num() ); } return; } } if ( verbose ) { fprintf( stderr, "[%u] Received state %d [coming from %d, ack %d]\n", (unsigned int)(timestamp() % 100000), (int)new_state.num, (int)inst.old_num(), (int)inst.ack_num() ); } received_states.push_back( new_state ); sender.set_ack_num( received_states.back().num ); sender.remote_heard( new_state.timestamp ); if ( !inst.diff().empty() ) { sender.set_data_ack(); } } } /* The sender uses throwaway_num to tell us the earliest received state that we need to keep around */ template void Transport::process_throwaway_until( uint64_t throwaway_num ) { typename list< TimestampedState >::iterator i = received_states.begin(); while ( i != received_states.end() ) { typename list< TimestampedState >::iterator inext = i; inext++; if ( i->num < throwaway_num ) { received_states.erase( i ); } i = inext; } fatal_assert( received_states.size() > 0 ); } template string Transport::get_remote_diff( void ) { /* find diff between last receiver state and current remote state, then rationalize states */ string ret( received_states.back().state.diff_from( last_receiver_state ) ); const RemoteState *oldest_receiver_state = &received_states.front().state; for ( typename list< TimestampedState >::reverse_iterator i = received_states.rbegin(); i != received_states.rend(); i++ ) { i->state.subtract( oldest_receiver_state ); } last_receiver_state = received_states.back().state; return ret; } mosh-1.2.5/src/network/networktransport.h000066400000000000000000000117471255051512600206000ustar00rootroot00000000000000/* Mosh: the mobile shell Copyright 2012 Keith Winstein This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . In addition, as a special exception, the copyright holders give permission to link the code of portions of this program with the OpenSSL library under certain conditions as described in each individual source file, and distribute linked combinations including the two. You must obey the GNU General Public License in all respects for all of the code used other than OpenSSL. If you modify file(s) with this exception, you may extend this exception to your version of the file(s), but you are not obligated to do so. If you do not wish to do so, delete this exception statement from your version. If you delete this exception statement from all source files in the program, then also delete it here. */ #ifndef NETWORK_TRANSPORT_HPP #define NETWORK_TRANSPORT_HPP #include #include #include #include #include #include "network.h" #include "transportsender.h" #include "transportfragment.h" namespace Network { template class Transport { private: /* the underlying, encrypted network connection */ Connection connection; /* sender side */ TransportSender sender; /* helper methods for recv() */ void process_throwaway_until( uint64_t throwaway_num ); /* simple receiver */ list< TimestampedState > received_states; uint64_t receiver_quench_timer; RemoteState last_receiver_state; /* the state we were in when user last queried state */ FragmentAssembly fragments; bool verbose; public: Transport( MyState &initial_state, RemoteState &initial_remote, const char *desired_ip, const char *desired_port ); Transport( MyState &initial_state, RemoteState &initial_remote, const char *key_str, const char *ip, const char *port ); /* Send data or an ack if necessary. */ void tick( void ) { sender.tick(); } /* Returns the number of ms to wait until next possible event. */ int wait_time( void ) { return sender.wait_time(); } /* Blocks waiting for a packet. */ void recv( void ); /* Find diff between last receiver state and current remote state, then rationalize states. */ string get_remote_diff( void ); /* Shut down other side of connection. */ /* Illegal to change current_state after this. */ void start_shutdown( void ) { sender.start_shutdown(); } bool shutdown_in_progress( void ) const { return sender.get_shutdown_in_progress(); } bool shutdown_acknowledged( void ) const { return sender.get_shutdown_acknowledged(); } bool shutdown_ack_timed_out( void ) const { return sender.shutdown_ack_timed_out(); } bool has_remote_addr( void ) const { return connection.get_has_remote_addr(); } /* Other side has requested shutdown and we have sent one ACK */ bool counterparty_shutdown_ack_sent( void ) const { return sender.get_counterparty_shutdown_acknowledged(); } std::string port( void ) const { return connection.port(); } string get_key( void ) const { return connection.get_key(); } MyState &get_current_state( void ) { return sender.get_current_state(); } void set_current_state( const MyState &x ) { sender.set_current_state( x ); } uint64_t get_remote_state_num( void ) const { return received_states.back().num; } const TimestampedState & get_latest_remote_state( void ) const { return received_states.back(); } const std::vector< int > fds( void ) const { return connection.fds(); } void set_verbose( void ) { sender.set_verbose(); verbose = true; } void set_send_delay( int new_delay ) { sender.set_send_delay( new_delay ); } uint64_t get_sent_state_acked_timestamp( void ) const { return sender.get_sent_state_acked_timestamp(); } uint64_t get_sent_state_acked( void ) const { return sender.get_sent_state_acked(); } uint64_t get_sent_state_last( void ) const { return sender.get_sent_state_last(); } unsigned int send_interval( void ) const { return sender.send_interval(); } const Addr &get_remote_addr( void ) const { return connection.get_remote_addr(); } socklen_t get_remote_addr_len( void ) const { return connection.get_remote_addr_len(); } const NetworkException *get_send_exception( void ) const { return connection.get_send_exception(); } }; } #endif mosh-1.2.5/src/network/transportfragment.cc000066400000000000000000000140561255051512600210440ustar00rootroot00000000000000/* Mosh: the mobile shell Copyright 2012 Keith Winstein This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . In addition, as a special exception, the copyright holders give permission to link the code of portions of this program with the OpenSSL library under certain conditions as described in each individual source file, and distribute linked combinations including the two. You must obey the GNU General Public License in all respects for all of the code used other than OpenSSL. If you modify file(s) with this exception, you may extend this exception to your version of the file(s), but you are not obligated to do so. If you do not wish to do so, delete this exception statement from your version. If you delete this exception statement from all source files in the program, then also delete it here. */ #include #include "byteorder.h" #include "transportfragment.h" #include "transportinstruction.pb.h" #include "compressor.h" #include "fatal_assert.h" using namespace Network; using namespace TransportBuffers; static string network_order_string( uint16_t host_order ) { uint16_t net_int = htobe16( host_order ); return string( (char *)&net_int, sizeof( net_int ) ); } static string network_order_string( uint64_t host_order ) { uint64_t net_int = htobe64( host_order ); return string( (char *)&net_int, sizeof( net_int ) ); } string Fragment::tostring( void ) { assert( initialized ); string ret; ret += network_order_string( id ); fatal_assert( !( fragment_num & 0x8000 ) ); /* effective limit on size of a terminal screen change or buffered user input */ uint16_t combined_fragment_num = ( final << 15 ) | fragment_num; ret += network_order_string( combined_fragment_num ); assert( ret.size() == frag_header_len ); ret += contents; return ret; } Fragment::Fragment( string &x ) : id( -1 ), fragment_num( -1 ), final( false ), initialized( true ), contents() { fatal_assert( x.size() >= frag_header_len ); contents = string( x.begin() + frag_header_len, x.end() ); uint64_t data64; uint16_t *data16 = (uint16_t *)x.data(); memcpy( &data64, x.data(), sizeof( data64 ) ); id = be64toh( data64 ); fragment_num = be16toh( data16[ 4 ] ); final = ( fragment_num & 0x8000 ) >> 15; fragment_num &= 0x7FFF; } bool FragmentAssembly::add_fragment( Fragment &frag ) { /* see if this is a totally new packet */ if ( current_id != frag.id ) { fragments.clear(); fragments.resize( frag.fragment_num + 1 ); fragments.at( frag.fragment_num ) = frag; fragments_arrived = 1; fragments_total = -1; /* unknown */ current_id = frag.id; } else { /* not a new packet */ /* see if we already have this fragment */ if ( (fragments.size() > frag.fragment_num) && (fragments.at( frag.fragment_num ).initialized) ) { /* make sure new version is same as what we already have */ assert( fragments.at( frag.fragment_num ) == frag ); } else { if ( (int)fragments.size() < frag.fragment_num + 1 ) { fragments.resize( frag.fragment_num + 1 ); } fragments.at( frag.fragment_num ) = frag; fragments_arrived++; } } if ( frag.final ) { fragments_total = frag.fragment_num + 1; assert( (int)fragments.size() <= fragments_total ); fragments.resize( fragments_total ); } if ( fragments_total != -1 ) { assert( fragments_arrived <= fragments_total ); } /* see if we're done */ return ( fragments_arrived == fragments_total ); } Instruction FragmentAssembly::get_assembly( void ) { assert( fragments_arrived == fragments_total ); string encoded; for ( int i = 0; i < fragments_total; i++ ) { assert( fragments.at( i ).initialized ); encoded += fragments.at( i ).contents; } Instruction ret; fatal_assert( ret.ParseFromString( get_compressor().uncompress_str( encoded ) ) ); fragments.clear(); fragments_arrived = 0; fragments_total = -1; return ret; } bool Fragment::operator==( const Fragment &x ) const { return ( id == x.id ) && ( fragment_num == x.fragment_num ) && ( final == x.final ) && ( initialized == x.initialized ) && ( contents == x.contents ); } vector Fragmenter::make_fragments( const Instruction &inst, int MTU ) { if ( (inst.old_num() != last_instruction.old_num()) || (inst.new_num() != last_instruction.new_num()) || (inst.ack_num() != last_instruction.ack_num()) || (inst.throwaway_num() != last_instruction.throwaway_num()) || (inst.chaff() != last_instruction.chaff()) || (inst.protocol_version() != last_instruction.protocol_version()) || (last_MTU != MTU) ) { next_instruction_id++; } if ( (inst.old_num() == last_instruction.old_num()) && (inst.new_num() == last_instruction.new_num()) ) { assert( inst.diff() == last_instruction.diff() ); } last_instruction = inst; last_MTU = MTU; string payload = get_compressor().compress_str( inst.SerializeAsString() ); uint16_t fragment_num = 0; vector ret; while ( !payload.empty() ) { string this_fragment; bool final = false; if ( int( payload.size() + HEADER_LEN ) > MTU ) { this_fragment = string( payload.begin(), payload.begin() + MTU - HEADER_LEN ); payload = string( payload.begin() + MTU - HEADER_LEN, payload.end() ); } else { this_fragment = payload; payload.clear(); final = true; } ret.push_back( Fragment( next_instruction_id, fragment_num++, final, this_fragment ) ); } return ret; } mosh-1.2.5/src/network/transportfragment.h000066400000000000000000000062531255051512600207060ustar00rootroot00000000000000/* Mosh: the mobile shell Copyright 2012 Keith Winstein This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . In addition, as a special exception, the copyright holders give permission to link the code of portions of this program with the OpenSSL library under certain conditions as described in each individual source file, and distribute linked combinations including the two. You must obey the GNU General Public License in all respects for all of the code used other than OpenSSL. If you modify file(s) with this exception, you may extend this exception to your version of the file(s), but you are not obligated to do so. If you do not wish to do so, delete this exception statement from your version. If you delete this exception statement from all source files in the program, then also delete it here. */ #ifndef TRANSPORT_FRAGMENT_HPP #define TRANSPORT_FRAGMENT_HPP #include #include #include #include "transportinstruction.pb.h" using std::vector; using std::string; using namespace TransportBuffers; namespace Network { static const int HEADER_LEN = 66; class Fragment { private: static const size_t frag_header_len = sizeof( uint64_t ) + sizeof( uint16_t ); public: uint64_t id; uint16_t fragment_num; bool final; bool initialized; string contents; Fragment() : id( -1 ), fragment_num( -1 ), final( false ), initialized( false ), contents() {} Fragment( uint64_t s_id, uint16_t s_fragment_num, bool s_final, string s_contents ) : id( s_id ), fragment_num( s_fragment_num ), final( s_final ), initialized( true ), contents( s_contents ) {} Fragment( string &x ); string tostring( void ); bool operator==( const Fragment &x ) const; }; class FragmentAssembly { private: vector fragments; uint64_t current_id; int fragments_arrived, fragments_total; public: FragmentAssembly() : fragments(), current_id( -1 ), fragments_arrived( 0 ), fragments_total( -1 ) {} bool add_fragment( Fragment &inst ); Instruction get_assembly( void ); }; class Fragmenter { private: uint64_t next_instruction_id; Instruction last_instruction; int last_MTU; public: Fragmenter() : next_instruction_id( 0 ), last_instruction(), last_MTU( -1 ) { last_instruction.set_old_num( -1 ); last_instruction.set_new_num( -1 ); } vector make_fragments( const Instruction &inst, int MTU ); uint64_t last_ack_sent( void ) const { return last_instruction.ack_num(); } }; } #endif mosh-1.2.5/src/network/transportsender.cc000066400000000000000000000275531255051512600205270ustar00rootroot00000000000000/* Mosh: the mobile shell Copyright 2012 Keith Winstein This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . In addition, as a special exception, the copyright holders give permission to link the code of portions of this program with the OpenSSL library under certain conditions as described in each individual source file, and distribute linked combinations including the two. You must obey the GNU General Public License in all respects for all of the code used other than OpenSSL. If you modify file(s) with this exception, you may extend this exception to your version of the file(s), but you are not obligated to do so. If you do not wish to do so, delete this exception statement from your version. If you delete this exception statement from all source files in the program, then also delete it here. */ #include #include #include #include #include #include "transportsender.h" #include "transportfragment.h" #include using namespace Network; using namespace std; template TransportSender::TransportSender( Connection *s_connection, MyState &initial_state ) : connection( s_connection ), current_state( initial_state ), sent_states( 1, TimestampedState( timestamp(), 0, initial_state ) ), assumed_receiver_state( sent_states.begin() ), fragmenter(), next_ack_time( timestamp() ), next_send_time( timestamp() ), verbose( false ), shutdown_in_progress( false ), shutdown_tries( 0 ), shutdown_start( -1 ), ack_num( 0 ), pending_data_ack( false ), SEND_MINDELAY( 8 ), last_heard( 0 ), prng(), mindelay_clock( -1 ) { } /* Try to send roughly two frames per RTT, bounded by limits on frame rate */ template unsigned int TransportSender::send_interval( void ) const { int SEND_INTERVAL = lrint( ceil( connection->get_SRTT() / 2.0 ) ); if ( SEND_INTERVAL < SEND_INTERVAL_MIN ) { SEND_INTERVAL = SEND_INTERVAL_MIN; } else if ( SEND_INTERVAL > SEND_INTERVAL_MAX ) { SEND_INTERVAL = SEND_INTERVAL_MAX; } return SEND_INTERVAL; } /* Housekeeping routine to calculate next send and ack times */ template void TransportSender::calculate_timers( void ) { uint64_t now = timestamp(); /* Update assumed receiver state */ update_assumed_receiver_state(); /* Cut out common prefix of all states */ rationalize_states(); if ( pending_data_ack && (next_ack_time > now + ACK_DELAY) ) { next_ack_time = now + ACK_DELAY; } if ( !(current_state == sent_states.back().state) ) { if ( mindelay_clock == uint64_t( -1 ) ) { mindelay_clock = now; } next_send_time = max( mindelay_clock + SEND_MINDELAY, sent_states.back().timestamp + send_interval() ); } else if ( !(current_state == assumed_receiver_state->state) && (last_heard + ACTIVE_RETRY_TIMEOUT > now) ) { next_send_time = sent_states.back().timestamp + send_interval(); if ( mindelay_clock != uint64_t( -1 ) ) { next_send_time = max( next_send_time, mindelay_clock + SEND_MINDELAY ); } } else if ( !(current_state == sent_states.front().state ) && (last_heard + ACTIVE_RETRY_TIMEOUT > now) ) { next_send_time = sent_states.back().timestamp + connection->timeout() + ACK_DELAY; } else { next_send_time = uint64_t(-1); } /* speed up shutdown sequence */ if ( shutdown_in_progress || (ack_num == uint64_t(-1)) ) { next_ack_time = sent_states.back().timestamp + send_interval(); } } /* How many ms to wait until next event */ template int TransportSender::wait_time( void ) { calculate_timers(); uint64_t next_wakeup = next_ack_time; if ( next_send_time < next_wakeup ) { next_wakeup = next_send_time; } uint64_t now = timestamp(); if ( !connection->get_has_remote_addr() ) { return INT_MAX; } if ( next_wakeup > now ) { return next_wakeup - now; } else { return 0; } } /* Send data or an empty ack if necessary */ template void TransportSender::tick( void ) { calculate_timers(); /* updates assumed receiver state and rationalizes */ if ( !connection->get_has_remote_addr() ) { return; } uint64_t now = timestamp(); if ( (now < next_ack_time) && (now < next_send_time) ) { return; } /* Determine if a new diff or empty ack needs to be sent */ string diff = current_state.diff_from( assumed_receiver_state->state ); attempt_prospective_resend_optimization( diff ); if ( verbose ) { /* verify diff has round-trip identity (modulo Unicode fallback rendering) */ MyState newstate( assumed_receiver_state->state ); newstate.apply_string( diff ); if ( current_state.compare( newstate ) ) { fprintf( stderr, "Warning, round-trip Instruction verification failed!\n" ); } } if ( diff.empty() && (now >= next_ack_time) ) { send_empty_ack(); mindelay_clock = uint64_t( -1 ); } else if ( !diff.empty() && ( (now >= next_send_time) || (now >= next_ack_time) ) ) { /* Send diffs or ack */ send_to_receiver( diff ); mindelay_clock = uint64_t( -1 ); } } template void TransportSender::send_empty_ack( void ) { uint64_t now = timestamp(); assert( now >= next_ack_time ); uint64_t new_num = sent_states.back().num + 1; /* special case for shutdown sequence */ if ( shutdown_in_progress ) { new_num = uint64_t( -1 ); } // sent_states.push_back( TimestampedState( sent_states.back().timestamp, new_num, current_state ) ); add_sent_state( now, new_num, current_state ); send_in_fragments( "", new_num ); next_ack_time = now + ACK_INTERVAL; next_send_time = uint64_t(-1); } template void TransportSender::add_sent_state( uint64_t the_timestamp, uint64_t num, MyState &state ) { sent_states.push_back( TimestampedState( the_timestamp, num, state ) ); if ( sent_states.size() > 32 ) { /* limit on state queue */ typename sent_states_type::iterator last = sent_states.end(); for ( int i = 0; i < 16; i++ ) { last--; } sent_states.erase( last ); /* erase state from middle of queue */ } } template void TransportSender::send_to_receiver( string diff ) { uint64_t new_num; if ( current_state == sent_states.back().state ) { /* previously sent */ new_num = sent_states.back().num; } else { /* new state */ new_num = sent_states.back().num + 1; } /* special case for shutdown sequence */ if ( shutdown_in_progress ) { new_num = uint64_t( -1 ); } if ( new_num == sent_states.back().num ) { sent_states.back().timestamp = timestamp(); } else { add_sent_state( timestamp(), new_num, current_state ); } send_in_fragments( diff, new_num ); // Can throw NetworkException /* successfully sent, probably */ /* ("probably" because the FIRST size-exceeded datagram doesn't get an error) */ assumed_receiver_state = sent_states.end(); assumed_receiver_state--; next_ack_time = timestamp() + ACK_INTERVAL; next_send_time = uint64_t(-1); } template void TransportSender::update_assumed_receiver_state( void ) { uint64_t now = timestamp(); /* start from what is known and give benefit of the doubt to unacknowledged states transmitted recently enough ago */ assumed_receiver_state = sent_states.begin(); typename list< TimestampedState >::iterator i = sent_states.begin(); i++; while ( i != sent_states.end() ) { assert( now >= i->timestamp ); if ( uint64_t(now - i->timestamp) < connection->timeout() + ACK_DELAY ) { assumed_receiver_state = i; } else { return; } i++; } } template void TransportSender::rationalize_states( void ) { const MyState * known_receiver_state = &sent_states.front().state; current_state.subtract( known_receiver_state ); for ( typename list< TimestampedState >::reverse_iterator i = sent_states.rbegin(); i != sent_states.rend(); i++ ) { i->state.subtract( known_receiver_state ); } } template const string TransportSender::make_chaff( void ) { const size_t CHAFF_MAX = 16; const size_t chaff_len = prng.uint8() % (CHAFF_MAX + 1); char chaff[ CHAFF_MAX ]; prng.fill( chaff, chaff_len ); return string( chaff, chaff_len ); } template void TransportSender::send_in_fragments( string diff, uint64_t new_num ) { Instruction inst; inst.set_protocol_version( MOSH_PROTOCOL_VERSION ); inst.set_old_num( assumed_receiver_state->num ); inst.set_new_num( new_num ); inst.set_ack_num( ack_num ); inst.set_throwaway_num( sent_states.front().num ); inst.set_diff( diff ); inst.set_chaff( make_chaff() ); if ( new_num == uint64_t(-1) ) { shutdown_tries++; } vector fragments = fragmenter.make_fragments( inst, connection->get_MTU() ); for ( vector::iterator i = fragments.begin(); i != fragments.end(); i++ ) { connection->send( i->tostring() ); if ( verbose ) { fprintf( stderr, "[%u] Sent [%d=>%d] id %d, frag %d ack=%d, throwaway=%d, len=%d, frame rate=%.2f, timeout=%d, srtt=%.1f\n", (unsigned int)(timestamp() % 100000), (int)inst.old_num(), (int)inst.new_num(), (int)i->id, (int)i->fragment_num, (int)inst.ack_num(), (int)inst.throwaway_num(), (int)i->contents.size(), 1000.0 / (double)send_interval(), (int)connection->timeout(), connection->get_SRTT() ); } } pending_data_ack = false; } template void TransportSender::process_acknowledgment_through( uint64_t ack_num ) { /* Ignore ack if we have culled the state it's acknowledging */ if ( sent_states.end() != find_if( sent_states.begin(), sent_states.end(), bind2nd( mem_fun_ref( &TimestampedState::num_eq ), ack_num ) ) ) { sent_states.remove_if( bind2nd( mem_fun_ref( &TimestampedState::num_lt ), ack_num ) ); } assert( !sent_states.empty() ); } /* give up on getting acknowledgement for shutdown */ template bool TransportSender::shutdown_ack_timed_out( void ) const { if ( shutdown_in_progress ) { if ( shutdown_tries >= SHUTDOWN_RETRIES ) { return true; } else if ( timestamp() - shutdown_start >= uint64_t( ACTIVE_RETRY_TIMEOUT ) ) { return true; } } return false; } /* Executed upon entry to new receiver state */ template void TransportSender::set_ack_num( uint64_t s_ack_num ) { ack_num = s_ack_num; } /* Investigate diff against known receiver state instead */ /* Mutates proposed_diff */ template void TransportSender::attempt_prospective_resend_optimization( string &proposed_diff ) { if ( assumed_receiver_state == sent_states.begin() ) { return; } string resend_diff = current_state.diff_from( sent_states.front().state ); /* We do a prophylactic resend if it would make the diff shorter, or if it would lengthen it by no more than 100 bytes and still be less than 1000 bytes. */ if ( (resend_diff.size() <= proposed_diff.size()) || ( (resend_diff.size() < 1000) && (resend_diff.size() - proposed_diff.size() < 100) ) ) { assumed_receiver_state = sent_states.begin(); proposed_diff = resend_diff; } } mosh-1.2.5/src/network/transportsender.h000066400000000000000000000133651255051512600203650ustar00rootroot00000000000000/* Mosh: the mobile shell Copyright 2012 Keith Winstein This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . In addition, as a special exception, the copyright holders give permission to link the code of portions of this program with the OpenSSL library under certain conditions as described in each individual source file, and distribute linked combinations including the two. You must obey the GNU General Public License in all respects for all of the code used other than OpenSSL. If you modify file(s) with this exception, you may extend this exception to your version of the file(s), but you are not obligated to do so. If you do not wish to do so, delete this exception statement from your version. If you delete this exception statement from all source files in the program, then also delete it here. */ #ifndef TRANSPORT_SENDER_HPP #define TRANSPORT_SENDER_HPP #include #include #include "network.h" #include "transportinstruction.pb.h" #include "transportstate.h" #include "transportfragment.h" #include "prng.h" using std::list; using std::pair; using namespace TransportBuffers; namespace Network { /* timing parameters */ const int SEND_INTERVAL_MIN = 20; /* ms between frames */ const int SEND_INTERVAL_MAX = 250; /* ms between frames */ const int ACK_INTERVAL = 3000; /* ms between empty acks */ const int ACK_DELAY = 100; /* ms before delayed ack */ const int SHUTDOWN_RETRIES = 16; /* number of shutdown packets to send before giving up */ const int ACTIVE_RETRY_TIMEOUT = 10000; /* attempt to resend at frame rate */ template class TransportSender { private: /* helper methods for tick() */ void update_assumed_receiver_state( void ); void attempt_prospective_resend_optimization( string &proposed_diff ); void rationalize_states( void ); void send_to_receiver( string diff ); void send_empty_ack( void ); void send_in_fragments( string diff, uint64_t new_num ); void add_sent_state( uint64_t the_timestamp, uint64_t num, MyState &state ); /* state of sender */ Connection *connection; MyState current_state; typedef list< TimestampedState > sent_states_type; sent_states_type sent_states; /* first element: known, acknowledged receiver state */ /* last element: last sent state */ /* somewhere in the middle: the assumed state of the receiver */ typename sent_states_type::iterator assumed_receiver_state; /* for fragment creation */ Fragmenter fragmenter; /* timing state */ uint64_t next_ack_time; uint64_t next_send_time; void calculate_timers( void ); bool verbose; bool shutdown_in_progress; int shutdown_tries; uint64_t shutdown_start; /* information about receiver state */ uint64_t ack_num; bool pending_data_ack; unsigned int SEND_MINDELAY; /* ms to collect all input */ uint64_t last_heard; /* last time received new state */ /* chaff to disguise instruction length */ PRNG prng; const string make_chaff( void ); uint64_t mindelay_clock; /* time of first pending change to current state */ public: /* constructor */ TransportSender( Connection *s_connection, MyState &initial_state ); /* Send data or an ack if necessary */ void tick( void ); /* Returns the number of ms to wait until next possible event. */ int wait_time( void ); /* Executed upon receipt of ack */ void process_acknowledgment_through( uint64_t ack_num ); /* Executed upon entry to new receiver state */ void set_ack_num( uint64_t s_ack_num ); /* Accelerate reply ack */ void set_data_ack( void ) { pending_data_ack = true; } /* Received something */ void remote_heard( uint64_t ts ) { last_heard = ts; } /* Starts shutdown sequence */ void start_shutdown( void ) { if ( !shutdown_in_progress ) { shutdown_start = timestamp(); shutdown_in_progress = true; } } /* Misc. getters and setters */ /* Cannot modify current_state while shutdown in progress */ MyState &get_current_state( void ) { assert( !shutdown_in_progress ); return current_state; } void set_current_state( const MyState &x ) { assert( !shutdown_in_progress ); current_state = x; } void set_verbose( void ) { verbose = true; } bool get_shutdown_in_progress( void ) const { return shutdown_in_progress; } bool get_shutdown_acknowledged( void ) const { return sent_states.front().num == uint64_t(-1); } bool get_counterparty_shutdown_acknowledged( void ) const { return fragmenter.last_ack_sent() == uint64_t(-1); } uint64_t get_sent_state_acked_timestamp( void ) const { return sent_states.front().timestamp; } uint64_t get_sent_state_acked( void ) const { return sent_states.front().num; } uint64_t get_sent_state_last( void ) const { return sent_states.back().num; } bool shutdown_ack_timed_out( void ) const; void set_send_delay( int new_delay ) { SEND_MINDELAY = new_delay; } unsigned int send_interval( void ) const; /* nonexistent methods to satisfy -Weffc++ */ TransportSender( const TransportSender &x ); TransportSender & operator=( const TransportSender &x ); }; } #endif mosh-1.2.5/src/network/transportstate.h000066400000000000000000000037141255051512600202220ustar00rootroot00000000000000/* Mosh: the mobile shell Copyright 2012 Keith Winstein This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . In addition, as a special exception, the copyright holders give permission to link the code of portions of this program with the OpenSSL library under certain conditions as described in each individual source file, and distribute linked combinations including the two. You must obey the GNU General Public License in all respects for all of the code used other than OpenSSL. If you modify file(s) with this exception, you may extend this exception to your version of the file(s), but you are not obligated to do so. If you do not wish to do so, delete this exception statement from your version. If you delete this exception statement from all source files in the program, then also delete it here. */ #ifndef TRANSPORT_STATE_HPP #define TRANSPORT_STATE_HPP namespace Network { template class TimestampedState { public: uint64_t timestamp; uint64_t num; State state; TimestampedState( uint64_t s_timestamp, uint64_t s_num, State &s_state ) : timestamp( s_timestamp ), num( s_num ), state( s_state ) {} /* For use with find_if, remove_if */ bool num_eq( uint64_t v ) const { return num == v; } bool num_lt( uint64_t v ) const { return num < v; } }; } #endif mosh-1.2.5/src/protobufs/000077500000000000000000000000001255051512600153015ustar00rootroot00000000000000mosh-1.2.5/src/protobufs/Makefile.am000066400000000000000000000010021255051512600173260ustar00rootroot00000000000000source = userinput.proto hostinput.proto transportinstruction.proto AM_CPPFLAGS = $(protobuf_CFLAGS) AM_CXXFLAGS = $(WARNING_CXXFLAGS) $(HARDEN_CFLAGS) $(MISC_CXXFLAGS) SUFFIXES = .proto .pb.cc .proto.pb.cc: $(AM_V_GEN)$(PROTOC) --cpp_out=. -I$(srcdir) $< noinst_LIBRARIES = libmoshprotos.a libmoshprotos_a_SOURCES = $(source) nodist_libmoshprotos_a_SOURCES = $(source:.proto=.pb.cc) $(source:.proto=.pb.h) BUILT_SOURCES = $(source:.proto=.pb.cc) CLEANFILES = $(source:.proto=.pb.cc) $(source:.proto=.pb.h) mosh-1.2.5/src/protobufs/hostinput.proto000066400000000000000000000007611255051512600204270ustar00rootroot00000000000000option optimize_for = LITE_RUNTIME; package HostBuffers; message HostMessage { repeated Instruction instruction = 1; } message Instruction { extensions 2 to max; } message HostBytes { optional bytes hoststring = 4; } message ResizeMessage { optional int32 width = 5; optional int32 height = 6; } message EchoAck { optional uint64 echo_ack_num = 8; } extend Instruction { optional HostBytes hostbytes = 2; optional ResizeMessage resize = 3; optional EchoAck echoack = 7; } mosh-1.2.5/src/protobufs/transportinstruction.proto000066400000000000000000000004741255051512600227310ustar00rootroot00000000000000option optimize_for = LITE_RUNTIME; package TransportBuffers; message Instruction { optional uint32 protocol_version = 1; optional uint64 old_num = 2; optional uint64 new_num = 3; optional uint64 ack_num = 4; optional uint64 throwaway_num = 5; optional bytes diff = 6; optional bytes chaff = 7; } mosh-1.2.5/src/protobufs/userinput.proto000066400000000000000000000006241255051512600204260ustar00rootroot00000000000000option optimize_for = LITE_RUNTIME; package ClientBuffers; message UserMessage { repeated Instruction instruction = 1; } message Instruction { extensions 2 to max; } message Keystroke { optional bytes keys = 4; } message ResizeMessage { optional int32 width = 5; optional int32 height = 6; } extend Instruction { optional Keystroke keystroke = 2; optional ResizeMessage resize = 3; } mosh-1.2.5/src/statesync/000077500000000000000000000000001255051512600152735ustar00rootroot00000000000000mosh-1.2.5/src/statesync/Makefile.am000066400000000000000000000004551255051512600173330ustar00rootroot00000000000000AM_CPPFLAGS = -I$(srcdir)/../util -I$(srcdir)/../terminal -I../protobufs $(protobuf_CFLAGS) AM_CXXFLAGS = $(WARNING_CXXFLAGS) $(PICKY_CXXFLAGS) $(HARDEN_CFLAGS) $(MISC_CXXFLAGS) noinst_LIBRARIES = libmoshstatesync.a libmoshstatesync_a_SOURCES = completeterminal.cc completeterminal.h user.cc user.h mosh-1.2.5/src/statesync/completeterminal.cc000066400000000000000000000144721255051512600211560ustar00rootroot00000000000000/* Mosh: the mobile shell Copyright 2012 Keith Winstein This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . In addition, as a special exception, the copyright holders give permission to link the code of portions of this program with the OpenSSL library under certain conditions as described in each individual source file, and distribute linked combinations including the two. You must obey the GNU General Public License in all respects for all of the code used other than OpenSSL. If you modify file(s) with this exception, you may extend this exception to your version of the file(s), but you are not obligated to do so. If you do not wish to do so, delete this exception statement from your version. If you delete this exception statement from all source files in the program, then also delete it here. */ #include "completeterminal.h" #include "fatal_assert.h" #include "hostinput.pb.h" #include using namespace std; using namespace Parser; using namespace Terminal; using namespace HostBuffers; string Complete::act( const string &str ) { for ( unsigned int i = 0; i < str.size(); i++ ) { /* parse octet into up to three actions */ list actions( parser.input( str[ i ] ) ); /* apply actions to terminal and delete them */ for ( list::iterator it = actions.begin(); it != actions.end(); it++ ) { Action *act = *it; act->act_on_terminal( &terminal ); delete act; } } return terminal.read_octets_to_host(); } string Complete::act( const Action *act ) { /* apply action to terminal */ act->act_on_terminal( &terminal ); return terminal.read_octets_to_host(); } /* interface for Network::Transport */ string Complete::diff_from( const Complete &existing ) const { HostBuffers::HostMessage output; if ( existing.get_echo_ack() != get_echo_ack() ) { assert( get_echo_ack() >= existing.get_echo_ack() ); Instruction *new_echo = output.add_instruction(); new_echo->MutableExtension( echoack )->set_echo_ack_num( get_echo_ack() ); } if ( !(existing.get_fb() == get_fb()) ) { if ( (existing.get_fb().ds.get_width() != terminal.get_fb().ds.get_width()) || (existing.get_fb().ds.get_height() != terminal.get_fb().ds.get_height()) ) { Instruction *new_res = output.add_instruction(); new_res->MutableExtension( resize )->set_width( terminal.get_fb().ds.get_width() ); new_res->MutableExtension( resize )->set_height( terminal.get_fb().ds.get_height() ); } Instruction *new_inst = output.add_instruction(); new_inst->MutableExtension( hostbytes )->set_hoststring( display.new_frame( true, existing.get_fb(), terminal.get_fb() ) ); } return output.SerializeAsString(); } void Complete::apply_string( string diff ) { HostBuffers::HostMessage input; fatal_assert( input.ParseFromString( diff ) ); for ( int i = 0; i < input.instruction_size(); i++ ) { if ( input.instruction( i ).HasExtension( hostbytes ) ) { string terminal_to_host = act( input.instruction( i ).GetExtension( hostbytes ).hoststring() ); assert( terminal_to_host.empty() ); /* server never interrogates client terminal */ } else if ( input.instruction( i ).HasExtension( resize ) ) { act( new Resize( input.instruction( i ).GetExtension( resize ).width(), input.instruction( i ).GetExtension( resize ).height() ) ); } else if ( input.instruction( i ).HasExtension( echoack ) ) { uint64_t inst_echo_ack_num = input.instruction( i ).GetExtension( echoack ).echo_ack_num(); assert( inst_echo_ack_num >= echo_ack ); echo_ack = inst_echo_ack_num; } } } bool Complete::operator==( Complete const &x ) const { // assert( parser == x.parser ); /* parser state is irrelevant for us */ return (terminal == x.terminal) && (echo_ack == x.echo_ack); } static bool old_ack(uint64_t newest_echo_ack, const pair p) { return p.first < newest_echo_ack; } bool Complete::set_echo_ack( uint64_t now ) { bool ret = false; uint64_t newest_echo_ack = 0; for ( input_history_type::const_iterator i = input_history.begin(); i != input_history.end(); i++ ) { if ( i->second < now - ECHO_TIMEOUT ) { newest_echo_ack = i->first; } } input_history.remove_if( bind1st( ptr_fun( old_ack ), newest_echo_ack ) ); if ( echo_ack != newest_echo_ack ) { ret = true; } echo_ack = newest_echo_ack; return ret; } void Complete::register_input_frame( uint64_t n, uint64_t now ) { input_history.push_back( make_pair( n, now ) ); } int Complete::wait_time( uint64_t now ) const { if ( input_history.size() < 2 ) { return INT_MAX; } input_history_type::const_iterator it = input_history.begin(); it++; uint64_t next_echo_ack_time = it->second + ECHO_TIMEOUT; if ( next_echo_ack_time <= now ) { return 0; } else { return next_echo_ack_time - now; } } bool Complete::compare( const Complete &other ) const { bool ret = false; for ( int x = 0; x < terminal.get_fb().ds.get_width(); x++ ) { for ( int y = 0; y < terminal.get_fb().ds.get_height(); y++ ) { if ( terminal.get_fb().get_cell( y, x )->compare( *other.terminal.get_fb().get_cell( y, x ) ) ) { fprintf( stderr, "Cell (%d, %d) differs.\n", y, x ); ret = true; } } } if ( (terminal.get_fb().ds.get_cursor_row() != other.terminal.get_fb().ds.get_cursor_row()) || (terminal.get_fb().ds.get_cursor_col() != other.terminal.get_fb().ds.get_cursor_col()) ) { fprintf( stderr, "Cursor mismatch: (%d, %d) vs. (%d, %d).\n", terminal.get_fb().ds.get_cursor_row(), terminal.get_fb().ds.get_cursor_col(), other.terminal.get_fb().ds.get_cursor_row(), other.terminal.get_fb().ds.get_cursor_col() ); ret = true; } return ret; } mosh-1.2.5/src/statesync/completeterminal.h000066400000000000000000000056311255051512600210150ustar00rootroot00000000000000/* Mosh: the mobile shell Copyright 2012 Keith Winstein This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . In addition, as a special exception, the copyright holders give permission to link the code of portions of this program with the OpenSSL library under certain conditions as described in each individual source file, and distribute linked combinations including the two. You must obey the GNU General Public License in all respects for all of the code used other than OpenSSL. If you modify file(s) with this exception, you may extend this exception to your version of the file(s), but you are not obligated to do so. If you do not wish to do so, delete this exception statement from your version. If you delete this exception statement from all source files in the program, then also delete it here. */ #ifndef COMPLETE_TERMINAL_HPP #define COMPLETE_TERMINAL_HPP #include #include #include "parser.h" #include "terminal.h" /* This class represents the complete terminal -- a UTF8Parser feeding Actions to an Emulator. */ namespace Terminal { class Complete { private: Parser::UTF8Parser parser; Terminal::Emulator terminal; Terminal::Display display; typedef std::list< std::pair > input_history_type; input_history_type input_history; uint64_t echo_ack; static const int ECHO_TIMEOUT = 50; /* for late ack */ public: Complete( size_t width, size_t height ) : parser(), terminal( width, height ), display( false ), input_history(), echo_ack( 0 ) {} std::string act( const std::string &str ); std::string act( const Parser::Action *act ); const Framebuffer & get_fb( void ) const { return terminal.get_fb(); } bool parser_grounded( void ) const { return parser.is_grounded(); } uint64_t get_echo_ack( void ) const { return echo_ack; } bool set_echo_ack( uint64_t now ); void register_input_frame( uint64_t n, uint64_t now ); int wait_time( uint64_t now ) const; /* interface for Network::Transport */ void subtract( const Complete * ) const {} std::string diff_from( const Complete &existing ) const; void apply_string( std::string diff ); bool operator==( const Complete &x ) const; bool compare( const Complete &other ) const; }; } #endif mosh-1.2.5/src/statesync/user.cc000066400000000000000000000105231255051512600165610ustar00rootroot00000000000000/* Mosh: the mobile shell Copyright 2012 Keith Winstein This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . In addition, as a special exception, the copyright holders give permission to link the code of portions of this program with the OpenSSL library under certain conditions as described in each individual source file, and distribute linked combinations including the two. You must obey the GNU General Public License in all respects for all of the code used other than OpenSSL. If you modify file(s) with this exception, you may extend this exception to your version of the file(s), but you are not obligated to do so. If you do not wish to do so, delete this exception statement from your version. If you delete this exception statement from all source files in the program, then also delete it here. */ #include #include #include "user.h" #include "fatal_assert.h" #include "userinput.pb.h" using namespace Parser; using namespace Network; using namespace ClientBuffers; void UserStream::subtract( const UserStream *prefix ) { // if we are subtracting ourself from ourself, just clear the deque if ( this == prefix ) { actions.clear(); return; } for ( deque::const_iterator i = prefix->actions.begin(); i != prefix->actions.end(); i++ ) { assert( this != prefix ); assert( !actions.empty() ); assert( *i == actions.front() ); actions.pop_front(); } } string UserStream::diff_from( const UserStream &existing ) const { deque::const_iterator my_it = actions.begin(); for ( deque::const_iterator i = existing.actions.begin(); i != existing.actions.end(); i++ ) { assert( my_it != actions.end() ); assert( *i == *my_it ); my_it++; } ClientBuffers::UserMessage output; while ( my_it != actions.end() ) { switch ( my_it->type ) { case UserByteType: { char the_byte = my_it->userbyte.c; /* can we combine this with a previous Keystroke? */ if ( (output.instruction_size() > 0) && (output.instruction( output.instruction_size() - 1 ).HasExtension( keystroke )) ) { output.mutable_instruction( output.instruction_size() - 1 )->MutableExtension( keystroke )->mutable_keys()->append( string( &the_byte, 1 ) ); } else { Instruction *new_inst = output.add_instruction(); new_inst->MutableExtension( keystroke )->set_keys( &the_byte, 1 ); } } break; case ResizeType: { Instruction *new_inst = output.add_instruction(); new_inst->MutableExtension( resize )->set_width( my_it->resize.width ); new_inst->MutableExtension( resize )->set_height( my_it->resize.height ); } break; default: assert( false ); break; } my_it++; } return output.SerializeAsString(); } void UserStream::apply_string( string diff ) { ClientBuffers::UserMessage input; fatal_assert( input.ParseFromString( diff ) ); for ( int i = 0; i < input.instruction_size(); i++ ) { if ( input.instruction( i ).HasExtension( keystroke ) ) { string the_bytes = input.instruction( i ).GetExtension( keystroke ).keys(); for ( unsigned int loc = 0; loc < the_bytes.size(); loc++ ) { actions.push_back( UserEvent( UserByte( the_bytes.at( loc ) ) ) ); } } else if ( input.instruction( i ).HasExtension( resize ) ) { actions.push_back( UserEvent( Resize( input.instruction( i ).GetExtension( resize ).width(), input.instruction( i ).GetExtension( resize ).height() ) ) ); } } } const Parser::Action *UserStream::get_action( unsigned int i ) const { switch( actions[ i ].type ) { case UserByteType: return &( actions[ i ].userbyte ); case ResizeType: return &( actions[ i ].resize ); default: assert( false ); return NULL; } } mosh-1.2.5/src/statesync/user.h000066400000000000000000000062241255051512600164260ustar00rootroot00000000000000/* Mosh: the mobile shell Copyright 2012 Keith Winstein This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . In addition, as a special exception, the copyright holders give permission to link the code of portions of this program with the OpenSSL library under certain conditions as described in each individual source file, and distribute linked combinations including the two. You must obey the GNU General Public License in all respects for all of the code used other than OpenSSL. If you modify file(s) with this exception, you may extend this exception to your version of the file(s), but you are not obligated to do so. If you do not wish to do so, delete this exception statement from your version. If you delete this exception statement from all source files in the program, then also delete it here. */ #ifndef USER_HPP #define USER_HPP #include #include #include #include #include "parseraction.h" using std::deque; using std::list; using std::string; namespace Network { enum UserEventType { UserByteType = 0, ResizeType = 1 }; class UserEvent { public: UserEventType type; Parser::UserByte userbyte; Parser::Resize resize; UserEvent( Parser::UserByte s_userbyte ) : type( UserByteType ), userbyte( s_userbyte ), resize( -1, -1 ) {} UserEvent( Parser::Resize s_resize ) : type( ResizeType ), userbyte( 0 ), resize( s_resize ) {} UserEvent() /* default constructor required by C++11 STL */ : type( UserByteType ), userbyte( 0 ), resize( -1, -1 ) { assert( false ); } bool operator==( const UserEvent &x ) const { return ( type == x.type ) && ( userbyte == x.userbyte ) && ( resize == x.resize ); } }; class UserStream { private: deque actions; public: UserStream() : actions() {} void push_back( Parser::UserByte s_userbyte ) { actions.push_back( UserEvent( s_userbyte ) ); } void push_back( Parser::Resize s_resize ) { actions.push_back( UserEvent( s_resize ) ); } bool empty( void ) const { return actions.empty(); } size_t size( void ) const { return actions.size(); } const Parser::Action *get_action( unsigned int i ) const; /* interface for Network::Transport */ void subtract( const UserStream *prefix ); string diff_from( const UserStream &existing ) const; void apply_string( string diff ); bool operator==( const UserStream &x ) const { return actions == x.actions; } bool compare( const UserStream & ) { return false; } }; } #endif mosh-1.2.5/src/terminal/000077500000000000000000000000001255051512600150715ustar00rootroot00000000000000mosh-1.2.5/src/terminal/Makefile.am000066400000000000000000000010521255051512600171230ustar00rootroot00000000000000AM_CPPFLAGS = -I$(srcdir)/../util $(TINFO_CFLAGS) AM_CXXFLAGS = $(WARNING_CXXFLAGS) $(PICKY_CXXFLAGS) $(HARDEN_CFLAGS) $(MISC_CXXFLAGS) noinst_LIBRARIES = libmoshterminal.a libmoshterminal_a_SOURCES = parseraction.cc parseraction.h parser.cc parser.h parserstate.cc parserstatefamily.h parserstate.h parsertransition.h terminal.cc terminaldispatcher.cc terminaldispatcher.h terminaldisplay.cc terminaldisplayinit.cc terminaldisplay.h terminalframebuffer.cc terminalframebuffer.h terminalfunctions.cc terminal.h terminaluserinput.cc terminaluserinput.h mosh-1.2.5/src/terminal/parser.cc000066400000000000000000000114521255051512600166770ustar00rootroot00000000000000/* Mosh: the mobile shell Copyright 2012 Keith Winstein This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . In addition, as a special exception, the copyright holders give permission to link the code of portions of this program with the OpenSSL library under certain conditions as described in each individual source file, and distribute linked combinations including the two. You must obey the GNU General Public License in all respects for all of the code used other than OpenSSL. If you modify file(s) with this exception, you may extend this exception to your version of the file(s), but you are not obligated to do so. If you do not wish to do so, delete this exception statement from your version. If you delete this exception statement from all source files in the program, then also delete it here. */ #include #include #include #include #include #include "parser.h" const Parser::StateFamily Parser::family; static void append_or_delete( Parser::Action *act, std::list &vec ) { assert( act ); if ( typeid( *act ) != typeid( Parser::Ignore ) ) { vec.push_back( act ); } else { delete act; } } std::list Parser::Parser::input( wchar_t ch ) { std::list ret; Transition tx = state->input( ch ); if ( tx.next_state != NULL ) { append_or_delete( state->exit(), ret ); } append_or_delete( tx.action, ret ); tx.action = NULL; if ( tx.next_state != NULL ) { append_or_delete( tx.next_state->enter(), ret ); state = tx.next_state; } return ret; } Parser::UTF8Parser::UTF8Parser() : parser(), buf_len( 0 ) { assert( BUF_SIZE >= (size_t)MB_CUR_MAX ); buf[0] = '\0'; } std::list Parser::UTF8Parser::input( char c ) { assert( buf_len < BUF_SIZE ); buf[ buf_len++ ] = c; /* This function will only work in a UTF-8 locale. */ wchar_t pwc; mbstate_t ps; memset( &ps, 0, sizeof( ps ) ); size_t total_bytes_parsed = 0; size_t orig_buf_len = buf_len; std::list ret; /* this routine is somewhat complicated in order to comply with Unicode 6.0, section 3.9, "Best Practices for using U+FFFD" */ while ( total_bytes_parsed != orig_buf_len ) { assert( total_bytes_parsed < orig_buf_len ); assert( buf_len > 0 ); size_t bytes_parsed = mbrtowc( &pwc, buf, buf_len, &ps ); /* this returns 0 when n = 0! */ if ( bytes_parsed == 0 ) { /* character was NUL, accept and clear buffer */ assert( buf_len == 1 ); buf_len = 0; pwc = L'\0'; bytes_parsed = 1; } else if ( bytes_parsed == (size_t) -1 ) { /* invalid sequence, use replacement character and try again with last char */ assert( errno == EILSEQ ); if ( buf_len > 1 ) { buf[ 0 ] = buf[ buf_len - 1 ]; bytes_parsed = buf_len - 1; buf_len = 1; } else { buf_len = 0; bytes_parsed = 1; } pwc = (wchar_t) 0xFFFD; } else if ( bytes_parsed == (size_t) -2 ) { /* can't parse incomplete multibyte character */ total_bytes_parsed += buf_len; continue; } else { /* parsed into pwc, accept */ assert( bytes_parsed <= buf_len ); memmove( buf, buf + bytes_parsed, buf_len - bytes_parsed ); buf_len = buf_len - bytes_parsed; } /* Cast to unsigned for checks, because some platforms (e.g. ARM) use uint32_t as wchar_t, causing compiler warning on "pwc > 0" check. */ uint64_t pwcheck = pwc; if ( pwcheck > 0x10FFFF ) { /* outside Unicode range */ pwc = (wchar_t) 0xFFFD; } if ( (pwcheck >= 0xD800) && (pwcheck <= 0xDFFF) ) { /* surrogate code point */ /* OS X unfortunately allows these sequences without EILSEQ, but they are ill-formed UTF-8 and we shouldn't repeat them to the user's terminal. */ pwc = (wchar_t) 0xFFFD; } std::list vec = parser.input( pwc ); ret.insert( ret.end(), vec.begin(), vec.end() ); total_bytes_parsed += bytes_parsed; } return ret; } Parser::Parser::Parser( const Parser &other ) : state( other.state ) {} Parser::Parser & Parser::Parser::operator=( const Parser &other ) { state = other.state; return *this; } mosh-1.2.5/src/terminal/parser.h000066400000000000000000000050731255051512600165430ustar00rootroot00000000000000/* Mosh: the mobile shell Copyright 2012 Keith Winstein This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . In addition, as a special exception, the copyright holders give permission to link the code of portions of this program with the OpenSSL library under certain conditions as described in each individual source file, and distribute linked combinations including the two. You must obey the GNU General Public License in all respects for all of the code used other than OpenSSL. If you modify file(s) with this exception, you may extend this exception to your version of the file(s), but you are not obligated to do so. If you do not wish to do so, delete this exception statement from your version. If you delete this exception statement from all source files in the program, then also delete it here. */ #ifndef PARSER_HPP #define PARSER_HPP /* Based on Paul Williams's parser, http://www.vt100.net/emu/dec_ansi_parser */ #include #include #include #include "parsertransition.h" #include "parseraction.h" #include "parserstate.h" #include "parserstatefamily.h" namespace Parser { extern const StateFamily family; class Parser { private: State const *state; public: Parser() : state( &family.s_Ground ) {} Parser( const Parser &other ); Parser & operator=( const Parser & ); ~Parser() {} std::list input( wchar_t ch ); bool operator==( const Parser &x ) const { return state == x.state; } bool is_grounded( void ) const { return state == &family.s_Ground; } }; static const size_t BUF_SIZE = 8; class UTF8Parser { private: Parser parser; char buf[ BUF_SIZE ]; size_t buf_len; public: UTF8Parser(); std::list input( char c ); bool operator==( const UTF8Parser &x ) const { return parser == x.parser; } bool is_grounded( void ) const { return parser.is_grounded(); } }; } #endif mosh-1.2.5/src/terminal/parseraction.cc000066400000000000000000000063741255051512600201040ustar00rootroot00000000000000/* Mosh: the mobile shell Copyright 2012 Keith Winstein This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . In addition, as a special exception, the copyright holders give permission to link the code of portions of this program with the OpenSSL library under certain conditions as described in each individual source file, and distribute linked combinations including the two. You must obey the GNU General Public License in all respects for all of the code used other than OpenSSL. If you modify file(s) with this exception, you may extend this exception to your version of the file(s), but you are not obligated to do so. If you do not wish to do so, delete this exception statement from your version. If you delete this exception statement from all source files in the program, then also delete it here. */ #include #include #include "parseraction.h" #include "terminal.h" using namespace Parser; std::string Action::str( void ) { char thechar[ 10 ] = { 0 }; if ( char_present ) { if ( iswprint( ch ) ) snprintf( thechar, 10, "(%lc)", (wint_t)ch ); else snprintf( thechar, 10, "(0x%x)", (unsigned int)ch ); } return name() + std::string( thechar ); } void Print::act_on_terminal( Terminal::Emulator *emu ) const { emu->print( this ); } void Execute::act_on_terminal( Terminal::Emulator *emu ) const { emu->execute( this ); } void Clear::act_on_terminal( Terminal::Emulator *emu ) const { emu->dispatch.clear( this ); } void Param::act_on_terminal( Terminal::Emulator *emu ) const { emu->dispatch.newparamchar( this ); } void Collect::act_on_terminal( Terminal::Emulator *emu ) const { emu->dispatch.collect( this ); } void CSI_Dispatch::act_on_terminal( Terminal::Emulator *emu ) const { emu->CSI_dispatch( this ); } void Esc_Dispatch::act_on_terminal( Terminal::Emulator *emu ) const { emu->Esc_dispatch( this ); } void OSC_Put::act_on_terminal( Terminal::Emulator *emu ) const { emu->dispatch.OSC_put( this ); } void OSC_Start::act_on_terminal( Terminal::Emulator *emu ) const { emu->dispatch.OSC_start( this ); } void OSC_End::act_on_terminal( Terminal::Emulator *emu ) const { emu->OSC_end( this ); } void UserByte::act_on_terminal( Terminal::Emulator *emu ) const { emu->dispatch.terminal_to_host.append( emu->user.input( this, emu->fb.ds.application_mode_cursor_keys ) ); } void Resize::act_on_terminal( Terminal::Emulator *emu ) const { emu->resize( width, height ); handled = true; } bool Action::operator==( const Action &other ) const { return ( char_present == other.char_present ) && ( ch == other.ch ) && ( handled == other.handled ); } mosh-1.2.5/src/terminal/parseraction.h000066400000000000000000000120221255051512600177310ustar00rootroot00000000000000/* Mosh: the mobile shell Copyright 2012 Keith Winstein This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . In addition, as a special exception, the copyright holders give permission to link the code of portions of this program with the OpenSSL library under certain conditions as described in each individual source file, and distribute linked combinations including the two. You must obey the GNU General Public License in all respects for all of the code used other than OpenSSL. If you modify file(s) with this exception, you may extend this exception to your version of the file(s), but you are not obligated to do so. If you do not wish to do so, delete this exception statement from your version. If you delete this exception statement from all source files in the program, then also delete it here. */ #ifndef PARSERACTION_HPP #define PARSERACTION_HPP #include namespace Terminal { class Emulator; } namespace Parser { class Action { public: bool char_present; wchar_t ch; mutable bool handled; std::string str( void ); virtual std::string name( void ) = 0; virtual void act_on_terminal( Terminal::Emulator * ) const {}; Action() : char_present( false ), ch( -1 ), handled( false ) {}; virtual ~Action() {}; virtual bool operator==( const Action &other ) const; }; class Ignore : public Action { public: std::string name( void ) { return std::string( "Ignore" ); } }; class Print : public Action { public: std::string name( void ) { return std::string( "Print" ); } void act_on_terminal( Terminal::Emulator *emu ) const; }; class Execute : public Action { public: std::string name( void ) { return std::string( "Execute" ); } void act_on_terminal( Terminal::Emulator *emu ) const; }; class Clear : public Action { public: std::string name( void ) { return std::string( "Clear" ); } void act_on_terminal( Terminal::Emulator *emu ) const; }; class Collect : public Action { public: std::string name( void ) { return std::string( "Collect" ); } void act_on_terminal( Terminal::Emulator *emu ) const; }; class Param : public Action { public: std::string name( void ) { return std::string( "Param" ); } void act_on_terminal( Terminal::Emulator *emu ) const; }; class Esc_Dispatch : public Action { public: std::string name( void ) { return std::string( "Esc_Dispatch" ); } void act_on_terminal( Terminal::Emulator *emu ) const; }; class CSI_Dispatch : public Action { public: std::string name( void ) { return std::string( "CSI_Dispatch" ); } void act_on_terminal( Terminal::Emulator *emu ) const; }; class Hook : public Action { public: std::string name( void ) { return std::string( "Hook" ); } }; class Put : public Action { public: std::string name( void ) { return std::string( "Put" ); } }; class Unhook : public Action { public: std::string name( void ) { return std::string( "Unhook" ); } }; class OSC_Start : public Action { public: std::string name( void ) { return std::string( "OSC_Start" ); } void act_on_terminal( Terminal::Emulator *emu ) const; }; class OSC_Put : public Action { public: std::string name( void ) { return std::string( "OSC_Put" ); } void act_on_terminal( Terminal::Emulator *emu ) const; }; class OSC_End : public Action { public: std::string name( void ) { return std::string( "OSC_End" ); } void act_on_terminal( Terminal::Emulator *emu ) const; }; class UserByte : public Action { /* user keystroke -- not part of the host-source state machine*/ public: char c; /* The user-source byte. We don't try to interpret the charset */ std::string name( void ) { return std::string( "UserByte" ); } void act_on_terminal( Terminal::Emulator *emu ) const; UserByte( int s_c ) : c( s_c ) {} bool operator==( const UserByte &other ) const { return c == other.c; } }; class Resize : public Action { /* resize event -- not part of the host-source state machine*/ public: size_t width, height; std::string name( void ) { return std::string( "Resize" ); } void act_on_terminal( Terminal::Emulator *emu ) const; Resize( size_t s_width, size_t s_height ) : width( s_width ), height( s_height ) {} bool operator==( const Resize &other ) const { return ( width == other.width ) && ( height == other.height ); } }; } #endif mosh-1.2.5/src/terminal/parserstate.cc000066400000000000000000000220621255051512600177370ustar00rootroot00000000000000/* Mosh: the mobile shell Copyright 2012 Keith Winstein This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . In addition, as a special exception, the copyright holders give permission to link the code of portions of this program with the OpenSSL library under certain conditions as described in each individual source file, and distribute linked combinations including the two. You must obey the GNU General Public License in all respects for all of the code used other than OpenSSL. If you modify file(s) with this exception, you may extend this exception to your version of the file(s), but you are not obligated to do so. If you do not wish to do so, delete this exception statement from your version. If you delete this exception statement from all source files in the program, then also delete it here. */ #include "parserstate.h" #include "parserstatefamily.h" using namespace Parser; Transition State::anywhere_rule( wchar_t ch ) const { if ( (ch == 0x18) || (ch == 0x1A) || ((0x80 <= ch) && (ch <= 0x8F)) || ((0x91 <= ch) && (ch <= 0x97)) || (ch == 0x99) || (ch == 0x9A) ) { return Transition( new Execute, &family->s_Ground ); } else if ( ch == 0x9C ) { return Transition( &family->s_Ground ); } else if ( ch == 0x1B ) { return Transition( &family->s_Escape ); } else if ( (ch == 0x98) || (ch == 0x9E) || (ch == 0x9F) ) { return Transition( &family->s_SOS_PM_APC_String ); } else if ( ch == 0x90 ) { return Transition( &family->s_DCS_Entry ); } else if ( ch == 0x9D ) { return Transition( &family->s_OSC_String ); } else if ( ch == 0x9B ) { return Transition( &family->s_CSI_Entry ); } return Transition(( State * )NULL, NULL ); /* don't allocate an Ignore action */ } Transition State::input( wchar_t ch ) const { Transition ret = anywhere_rule( ch ); if ( !ret.next_state ) { if ( ch >= 0xA0 ) { ret = this->input_state_rule( 0x41 ); } else { ret = this->input_state_rule( ch ); } } ret.action->char_present = true; ret.action->ch = ch; return ret; } static bool C0_prime( wchar_t ch ) { return ( (ch <= 0x17) || (ch == 0x19) || ( (0x1C <= ch) && (ch <= 0x1F) ) ); } static bool GLGR ( wchar_t ch ) { return ( ( (0x20 <= ch) && (ch <= 0x7F) ) /* GL area */ || ( (0xA0 <= ch) && (ch <= 0xFF) ) ); /* GR area */ } Transition Ground::input_state_rule( wchar_t ch ) const { if ( C0_prime( ch ) ) { return Transition( new Execute ); } if ( GLGR( ch ) ) { return Transition( new Print ); } return Transition(); } Action *Escape::enter( void ) const { return new Clear; } Transition Escape::input_state_rule( wchar_t ch ) const { if ( C0_prime( ch ) ) { return Transition( new Execute ); } if ( (0x20 <= ch) && (ch <= 0x2F) ) { return Transition( new Collect, &family->s_Escape_Intermediate ); } if ( ( (0x30 <= ch) && (ch <= 0x4F) ) || ( (0x51 <= ch) && (ch <= 0x57) ) || ( ch == 0x59 ) || ( ch == 0x5A ) || ( ch == 0x5C ) || ( (0x60 <= ch) && (ch <= 0x7E) ) ) { return Transition( new Esc_Dispatch, &family->s_Ground ); } if ( ch == 0x5B ) { return Transition( &family->s_CSI_Entry ); } if ( ch == 0x5D ) { return Transition( &family->s_OSC_String ); } if ( ch == 0x50 ) { return Transition( &family->s_DCS_Entry ); } if ( (ch == 0x58) || (ch == 0x5E) || (ch == 0x5F) ) { return Transition( &family->s_SOS_PM_APC_String ); } return Transition(); } Transition Escape_Intermediate::input_state_rule( wchar_t ch ) const { if ( C0_prime( ch ) ) { return Transition( new Execute ); } if ( (0x20 <= ch) && (ch <= 0x2F) ) { return Transition( new Collect ); } if ( (0x30 <= ch) && (ch <= 0x7E) ) { return Transition( new Esc_Dispatch, &family->s_Ground ); } return Transition(); } Action *CSI_Entry::enter( void ) const { return new Clear; } Transition CSI_Entry::input_state_rule( wchar_t ch ) const { if ( C0_prime( ch ) ) { return Transition( new Execute ); } if ( (0x40 <= ch) && (ch <= 0x7E) ) { return Transition( new CSI_Dispatch, &family->s_Ground ); } if ( ( (0x30 <= ch) && (ch <= 0x39) ) || ( ch == 0x3B ) ) { return Transition( new Param, &family->s_CSI_Param ); } if ( (0x3C <= ch) && (ch <= 0x3F) ) { return Transition( new Collect, &family->s_CSI_Param ); } if ( ch == 0x3A ) { return Transition( &family->s_CSI_Ignore ); } if ( (0x20 <= ch) && (ch <= 0x2F) ) { return Transition( new Collect, &family->s_CSI_Intermediate ); } return Transition(); } Transition CSI_Param::input_state_rule( wchar_t ch ) const { if ( C0_prime( ch ) ) { return Transition( new Execute ); } if ( ( (0x30 <= ch) && (ch <= 0x39) ) || ( ch == 0x3B ) ) { return Transition( new Param ); } if ( ( ch == 0x3A ) || ( (0x3C <= ch) && (ch <= 0x3F) ) ) { return Transition( &family->s_CSI_Ignore ); } if ( (0x20 <= ch) && (ch <= 0x2F) ) { return Transition( new Collect, &family->s_CSI_Intermediate ); } if ( (0x40 <= ch) && (ch <= 0x7E) ) { return Transition( new CSI_Dispatch, &family->s_Ground ); } return Transition(); } Transition CSI_Intermediate::input_state_rule( wchar_t ch ) const { if ( C0_prime( ch ) ) { return Transition( new Execute ); } if ( (0x20 <= ch) && (ch <= 0x2F) ) { return Transition( new Collect ); } if ( (0x40 <= ch) && (ch <= 0x7E) ) { return Transition( new CSI_Dispatch, &family->s_Ground ); } if ( (0x30 <= ch) && (ch <= 0x3F) ) { return Transition( &family->s_CSI_Ignore ); } return Transition(); } Transition CSI_Ignore::input_state_rule( wchar_t ch ) const { if ( C0_prime( ch ) ) { return Transition( new Execute ); } if ( (0x40 <= ch) && (ch <= 0x7E) ) { return Transition( &family->s_Ground ); } return Transition(); } Action *DCS_Entry::enter( void ) const { return new Clear; } Transition DCS_Entry::input_state_rule( wchar_t ch ) const { if ( (0x20 <= ch) && (ch <= 0x2F) ) { return Transition( new Collect, &family->s_DCS_Intermediate ); } if ( ch == 0x3A ) { return Transition( &family->s_DCS_Ignore ); } if ( ( (0x30 <= ch) && (ch <= 0x39) ) || ( ch == 0x3B ) ) { return Transition( new Param, &family->s_DCS_Param ); } if ( (0x3C <= ch) && (ch <= 0x3F) ) { return Transition( new Collect, &family->s_DCS_Param ); } if ( (0x40 <= ch) && (ch <= 0x7E) ) { return Transition( &family->s_DCS_Passthrough ); } return Transition(); } Transition DCS_Param::input_state_rule( wchar_t ch ) const { if ( ( (0x30 <= ch) && (ch <= 0x39) ) || ( ch == 0x3B ) ) { return Transition( new Param ); } if ( ( ch == 0x3A ) || ( (0x3C <= ch) && (ch <= 0x3F) ) ) { return Transition( &family->s_DCS_Ignore ); } if ( (0x20 <= ch) && (ch <= 0x2F) ) { return Transition( new Collect, &family->s_DCS_Intermediate ); } if ( (0x40 <= ch) && (ch <= 0x7E) ) { return Transition( &family->s_DCS_Passthrough ); } return Transition(); } Transition DCS_Intermediate::input_state_rule( wchar_t ch ) const { if ( (0x20 <= ch) && (ch <= 0x2F) ) { return Transition( new Collect ); } if ( (0x40 <= ch) && (ch <= 0x7E) ) { return Transition( &family->s_DCS_Passthrough ); } if ( (0x30 <= ch) && (ch <= 0x3F) ) { return Transition( &family->s_DCS_Ignore ); } return Transition(); } Action *DCS_Passthrough::enter( void ) const { return new Hook; } Action *DCS_Passthrough::exit( void ) const { return new Unhook; } Transition DCS_Passthrough::input_state_rule( wchar_t ch ) const { if ( C0_prime( ch ) || ( (0x20 <= ch) && (ch <= 0x7E) ) ) { return Transition( new Put ); } if ( ch == 0x9C ) { return Transition( &family->s_Ground ); } return Transition(); } Transition DCS_Ignore::input_state_rule( wchar_t ch ) const { if ( ch == 0x9C ) { return Transition( &family->s_Ground ); } return Transition(); } Action *OSC_String::enter( void ) const { return new OSC_Start; } Action *OSC_String::exit( void ) const { return new OSC_End; } Transition OSC_String::input_state_rule( wchar_t ch ) const { if ( (0x20 <= ch) && (ch <= 0x7F) ) { return Transition( new OSC_Put ); } if ( (ch == 0x9C) || (ch == 0x07) ) { /* 0x07 is xterm non-ANSI variant */ return Transition( &family->s_Ground ); } return Transition(); } Transition SOS_PM_APC_String::input_state_rule( wchar_t ch ) const { if ( ch == 0x9C ) { return Transition( &family->s_Ground ); } return Transition(); } mosh-1.2.5/src/terminal/parserstate.h000066400000000000000000000072301255051512600176010ustar00rootroot00000000000000/* Mosh: the mobile shell Copyright 2012 Keith Winstein This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . In addition, as a special exception, the copyright holders give permission to link the code of portions of this program with the OpenSSL library under certain conditions as described in each individual source file, and distribute linked combinations including the two. You must obey the GNU General Public License in all respects for all of the code used other than OpenSSL. If you modify file(s) with this exception, you may extend this exception to your version of the file(s), but you are not obligated to do so. If you do not wish to do so, delete this exception statement from your version. If you delete this exception statement from all source files in the program, then also delete it here. */ #ifndef PARSERSTATE_HPP #define PARSERSTATE_HPP #include "parsertransition.h" namespace Parser { class StateFamily; class State { protected: virtual Transition input_state_rule( wchar_t ch ) const = 0; StateFamily *family; private: Transition anywhere_rule( wchar_t ch ) const; public: void setfamily( StateFamily *s_family ) { family = s_family; } Transition input( wchar_t ch ) const; virtual Action *enter( void ) const { return new Ignore; } virtual Action *exit( void ) const { return new Ignore; } State() : family( NULL ) {}; virtual ~State() {}; State( const State & ); State & operator=( const State & ); }; class Ground : public State { Transition input_state_rule( wchar_t ch ) const; }; class Escape : public State { Action *enter( void ) const; Transition input_state_rule( wchar_t ch ) const; }; class Escape_Intermediate : public State { Transition input_state_rule( wchar_t ch ) const; }; class CSI_Entry : public State { Action *enter( void ) const; Transition input_state_rule( wchar_t ch ) const; }; class CSI_Param : public State { Transition input_state_rule( wchar_t ch ) const; }; class CSI_Intermediate : public State { Transition input_state_rule( wchar_t ch ) const; }; class CSI_Ignore : public State { Transition input_state_rule( wchar_t ch ) const; }; class DCS_Entry : public State { Action *enter( void ) const; Transition input_state_rule( wchar_t ch ) const; }; class DCS_Param : public State { Transition input_state_rule( wchar_t ch ) const; }; class DCS_Intermediate : public State { Transition input_state_rule( wchar_t ch ) const; }; class DCS_Passthrough : public State { Action *enter( void ) const; Transition input_state_rule( wchar_t ch ) const; Action *exit( void ) const; }; class DCS_Ignore : public State { Transition input_state_rule( wchar_t ch ) const; }; class OSC_String : public State { Action *enter( void ) const; Transition input_state_rule( wchar_t ch ) const; Action *exit( void ) const; }; class SOS_PM_APC_String : public State { Transition input_state_rule( wchar_t ch ) const; }; } #endif mosh-1.2.5/src/terminal/parserstatefamily.h000066400000000000000000000055451255051512600210120ustar00rootroot00000000000000/* Mosh: the mobile shell Copyright 2012 Keith Winstein This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . In addition, as a special exception, the copyright holders give permission to link the code of portions of this program with the OpenSSL library under certain conditions as described in each individual source file, and distribute linked combinations including the two. You must obey the GNU General Public License in all respects for all of the code used other than OpenSSL. If you modify file(s) with this exception, you may extend this exception to your version of the file(s), but you are not obligated to do so. If you do not wish to do so, delete this exception statement from your version. If you delete this exception statement from all source files in the program, then also delete it here. */ #ifndef PARSERSTATEFAMILY_HPP #define PARSERSTATEFAMILY_HPP #include "parserstate.h" namespace Parser { class StateFamily { public: Ground s_Ground; Escape s_Escape; Escape_Intermediate s_Escape_Intermediate; CSI_Entry s_CSI_Entry; CSI_Param s_CSI_Param; CSI_Intermediate s_CSI_Intermediate; CSI_Ignore s_CSI_Ignore; DCS_Entry s_DCS_Entry; DCS_Param s_DCS_Param; DCS_Intermediate s_DCS_Intermediate; DCS_Passthrough s_DCS_Passthrough; DCS_Ignore s_DCS_Ignore; OSC_String s_OSC_String; SOS_PM_APC_String s_SOS_PM_APC_String; StateFamily() : s_Ground(), s_Escape(), s_Escape_Intermediate(), s_CSI_Entry(), s_CSI_Param(), s_CSI_Intermediate(), s_CSI_Ignore(), s_DCS_Entry(), s_DCS_Param(), s_DCS_Intermediate(), s_DCS_Passthrough(), s_DCS_Ignore(), s_OSC_String(), s_SOS_PM_APC_String() { s_Ground.setfamily( this ); s_Escape.setfamily( this ); s_Escape_Intermediate.setfamily( this ); s_CSI_Entry.setfamily( this ); s_CSI_Param.setfamily( this ); s_CSI_Intermediate.setfamily( this ); s_CSI_Ignore.setfamily( this ); s_DCS_Entry.setfamily( this ); s_DCS_Param.setfamily( this ); s_DCS_Intermediate.setfamily( this ); s_DCS_Passthrough.setfamily( this ); s_DCS_Ignore.setfamily( this ); s_OSC_String.setfamily( this ); s_SOS_PM_APC_String.setfamily( this ); } }; } #endif mosh-1.2.5/src/terminal/parsertransition.h000066400000000000000000000050341255051512600206530ustar00rootroot00000000000000/* Mosh: the mobile shell Copyright 2012 Keith Winstein This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . In addition, as a special exception, the copyright holders give permission to link the code of portions of this program with the OpenSSL library under certain conditions as described in each individual source file, and distribute linked combinations including the two. You must obey the GNU General Public License in all respects for all of the code used other than OpenSSL. If you modify file(s) with this exception, you may extend this exception to your version of the file(s), but you are not obligated to do so. If you do not wish to do so, delete this exception statement from your version. If you delete this exception statement from all source files in the program, then also delete it here. */ #ifndef PARSERTRANSITION_HPP #define PARSERTRANSITION_HPP #include #include "parseraction.h" namespace Parser { class State; class Transition { public: // Transition is only a courier for an Action; it should // never create/delete one on its own. Action *action; State *next_state; Transition( const Transition &x ) : action( x.action ), next_state( x.next_state ) {} Transition & operator=( const Transition &t ) { action = t.action; next_state = t.next_state; return *this; } virtual ~Transition() { // Indicate to checkers that we don't own *action anymore action = NULL; } Transition( Action *s_action=new Ignore, State *s_next_state=NULL ) : action( s_action ), next_state( s_next_state ) {} // This is only ever used in the 1-argument form; // we use this instead of an initializer to // tell Coverity the object never owns *action. Transition( State *s_next_state, Action *s_action=new Ignore ) : action( s_action ), next_state( s_next_state ) {} }; } #endif mosh-1.2.5/src/terminal/terminal.cc000066400000000000000000000123251255051512600172160ustar00rootroot00000000000000/* Mosh: the mobile shell Copyright 2012 Keith Winstein This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . In addition, as a special exception, the copyright holders give permission to link the code of portions of this program with the OpenSSL library under certain conditions as described in each individual source file, and distribute linked combinations including the two. You must obey the GNU General Public License in all respects for all of the code used other than OpenSSL. If you modify file(s) with this exception, you may extend this exception to your version of the file(s), but you are not obligated to do so. If you do not wish to do so, delete this exception statement from your version. If you delete this exception statement from all source files in the program, then also delete it here. */ #include #include #include #include #include #include "terminal.h" #include "swrite.h" using namespace Terminal; Emulator::Emulator( size_t s_width, size_t s_height ) : fb( s_width, s_height ), dispatch(), user() {} std::string Emulator::read_octets_to_host( void ) { std::string ret = dispatch.terminal_to_host; dispatch.terminal_to_host.clear(); return ret; } void Emulator::execute( const Parser::Execute *act ) { dispatch.dispatch( CONTROL, act, &fb ); } void Emulator::print( const Parser::Print *act ) { assert( act->char_present ); int chwidth = act->ch == L'\0' ? -1 : wcwidth( act->ch ); Cell *this_cell = fb.get_mutable_cell(); Cell *combining_cell = fb.get_combining_cell(); /* can be null if we were resized */ switch ( chwidth ) { case 1: /* normal character */ case 2: /* wide character */ if ( fb.ds.auto_wrap_mode && fb.ds.next_print_will_wrap ) { fb.get_mutable_row( -1 )->set_wrap( true ); fb.ds.move_col( 0 ); fb.move_rows_autoscroll( 1 ); } else if ( fb.ds.auto_wrap_mode && (chwidth == 2) && (fb.ds.get_cursor_col() == fb.ds.get_width() - 1) ) { /* wrap 2-cell chars if no room, even without will-wrap flag */ fb.reset_cell( this_cell ); fb.get_mutable_row( -1 )->set_wrap( false ); /* There doesn't seem to be a consistent way to get the downstream terminal emulator to set the wrap-around copy-and-paste flag on a row that ends with an empty cell because a wide char was wrapped to the next line. */ fb.ds.move_col( 0 ); fb.move_rows_autoscroll( 1 ); } if ( fb.ds.insert_mode ) { for ( int i = 0; i < chwidth; i++ ) { fb.insert_cell( fb.ds.get_cursor_row(), fb.ds.get_cursor_col() ); } } this_cell = fb.get_mutable_cell(); fb.reset_cell( this_cell ); this_cell->contents.push_back( act->ch ); this_cell->width = chwidth; fb.apply_renditions_to_current_cell(); if ( chwidth == 2 ) { /* erase overlapped cell */ if ( fb.ds.get_cursor_col() + 1 < fb.ds.get_width() ) { fb.reset_cell( fb.get_mutable_cell( fb.ds.get_cursor_row(), fb.ds.get_cursor_col() + 1 ) ); } } fb.ds.move_col( chwidth, true, true ); act->handled = true; break; case 0: /* combining character */ if ( combining_cell == NULL ) { /* character is now offscreen */ act->handled = true; break; } if ( combining_cell->contents.size() == 0 ) { /* cell starts with combining character */ assert( this_cell == combining_cell ); assert( combining_cell->width == 1 ); combining_cell->fallback = true; fb.ds.move_col( 1, true, true ); } if ( combining_cell->contents.size() < 16 ) { /* seems like a reasonable limit on combining characters */ combining_cell->contents.push_back( act->ch ); } act->handled = true; break; case -1: /* unprintable character */ break; default: assert( false ); break; } } void Emulator::CSI_dispatch( const Parser::CSI_Dispatch *act ) { dispatch.dispatch( CSI, act, &fb ); } void Emulator::OSC_end( const Parser::OSC_End *act ) { dispatch.OSC_dispatch( act, &fb ); } void Emulator::Esc_dispatch( const Parser::Esc_Dispatch *act ) { /* handle 7-bit ESC-encoding of C1 control characters */ if ( (dispatch.get_dispatch_chars().size() == 0) && (0x40 <= act->ch) && (act->ch <= 0x5F) ) { Parser::Esc_Dispatch act2 = *act; act2.ch += 0x40; dispatch.dispatch( CONTROL, &act2, &fb ); } else { dispatch.dispatch( ESCAPE, act, &fb ); } } void Emulator::resize( size_t s_width, size_t s_height ) { fb.resize( s_width, s_height ); } bool Emulator::operator==( Emulator const &x ) const { /* dispatcher and user are irrelevant for us */ return ( fb == x.fb ); } mosh-1.2.5/src/terminal/terminal.h000066400000000000000000000063041255051512600170600ustar00rootroot00000000000000/* Mosh: the mobile shell Copyright 2012 Keith Winstein This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . In addition, as a special exception, the copyright holders give permission to link the code of portions of this program with the OpenSSL library under certain conditions as described in each individual source file, and distribute linked combinations including the two. You must obey the GNU General Public License in all respects for all of the code used other than OpenSSL. If you modify file(s) with this exception, you may extend this exception to your version of the file(s), but you are not obligated to do so. If you do not wish to do so, delete this exception statement from your version. If you delete this exception statement from all source files in the program, then also delete it here. */ #ifndef TERMINAL_CPP #define TERMINAL_CPP #include #include #include #include #include "parseraction.h" #include "terminalframebuffer.h" #include "terminaldispatcher.h" #include "terminaluserinput.h" #include "terminaldisplay.h" namespace Terminal { class Emulator { friend void Parser::Print::act_on_terminal( Emulator * ) const; friend void Parser::Execute::act_on_terminal( Emulator * ) const; friend void Parser::Clear::act_on_terminal( Emulator * ) const; friend void Parser::Param::act_on_terminal( Emulator * ) const; friend void Parser::Collect::act_on_terminal( Emulator * ) const; friend void Parser::CSI_Dispatch::act_on_terminal( Emulator * ) const; friend void Parser::Esc_Dispatch::act_on_terminal( Emulator * ) const; friend void Parser::OSC_Start::act_on_terminal( Emulator * ) const; friend void Parser::OSC_Put::act_on_terminal( Emulator * ) const; friend void Parser::OSC_End::act_on_terminal( Emulator * ) const; friend void Parser::UserByte::act_on_terminal( Emulator * ) const; friend void Parser::Resize::act_on_terminal( Emulator * ) const; private: Framebuffer fb; Dispatcher dispatch; UserInput user; /* action methods */ void print( const Parser::Print *act ); void execute( const Parser::Execute *act ); void CSI_dispatch( const Parser::CSI_Dispatch *act ); void Esc_dispatch( const Parser::Esc_Dispatch *act ); void OSC_end( const Parser::OSC_End *act ); void resize( size_t s_width, size_t s_height ); public: Emulator( size_t s_width, size_t s_height ); std::string read_octets_to_host( void ); const Framebuffer & get_fb( void ) const { return fb; } bool operator==( Emulator const &x ) const; }; } #endif mosh-1.2.5/src/terminal/terminaldispatcher.cc000066400000000000000000000154371255051512600212740ustar00rootroot00000000000000/* Mosh: the mobile shell Copyright 2012 Keith Winstein This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . In addition, as a special exception, the copyright holders give permission to link the code of portions of this program with the OpenSSL library under certain conditions as described in each individual source file, and distribute linked combinations including the two. You must obey the GNU General Public License in all respects for all of the code used other than OpenSSL. If you modify file(s) with this exception, you may extend this exception to your version of the file(s), but you are not obligated to do so. If you do not wish to do so, delete this exception statement from your version. If you delete this exception statement from all source files in the program, then also delete it here. */ #include #include #include #include #include #include "terminaldispatcher.h" #include "parseraction.h" #include "terminalframebuffer.h" using namespace Terminal; Dispatcher::Dispatcher() : params(), parsed_params(), parsed( false ), dispatch_chars(), OSC_string(), terminal_to_host() {} void Dispatcher::newparamchar( const Parser::Param *act ) { assert( act->char_present ); assert( (act->ch == ';') || ( (act->ch >= '0') && (act->ch <= '9') ) ); if ( params.length() < 100 ) { /* enough for 16 five-char params plus 15 semicolons */ params.push_back( act->ch ); act->handled = true; } parsed = false; } void Dispatcher::collect( const Parser::Collect *act ) { assert( act->char_present ); if ( ( dispatch_chars.length() < 8 ) /* never should need more than 2 */ && ( act->ch <= 255 ) ) { /* ignore non-8-bit */ dispatch_chars.push_back( act->ch ); act->handled = true; } } void Dispatcher::clear( const Parser::Clear *act ) { params.clear(); dispatch_chars.clear(); parsed = false; act->handled = true; } void Dispatcher::parse_params( void ) { if ( parsed ) { return; } parsed_params.clear(); const char *str = params.c_str(); const char *segment_begin = str; while ( 1 ) { const char *segment_end = strchr( segment_begin, ';' ); if ( segment_end == NULL ) { break; } errno = 0; char *endptr; long val = strtol( segment_begin, &endptr, 10 ); if ( endptr == segment_begin ) { val = -1; } if ( val > PARAM_MAX || errno == ERANGE ) { val = -1; errno = 0; } if ( errno == 0 || segment_begin == endptr ) { parsed_params.push_back( val ); } segment_begin = segment_end + 1; } /* get last param */ errno = 0; char *endptr; long val = strtol( segment_begin, &endptr, 10 ); if ( endptr == segment_begin ) { val = -1; } if ( val > PARAM_MAX || errno == ERANGE ) { val = -1; errno = 0; } if ( errno == 0 || segment_begin == endptr ) { parsed_params.push_back( val ); } parsed = true; } int Dispatcher::getparam( size_t N, int defaultval ) { int ret = defaultval; if ( !parsed ) { parse_params(); } if ( parsed_params.size() > N ) { ret = parsed_params[ N ]; } if ( ret < 1 ) ret = defaultval; return ret; } int Dispatcher::param_count( void ) { if ( !parsed ) { parse_params(); } return parsed_params.size(); } std::string Dispatcher::str( void ) { char assum[ 64 ]; snprintf( assum, 64, "[dispatch=\"%s\" params=\"%s\"]", dispatch_chars.c_str(), params.c_str() ); return std::string( assum ); } /* construct on first use to avoid static initialization order crash */ DispatchRegistry & Terminal::get_global_dispatch_registry( void ) { static DispatchRegistry global_dispatch_registry; return global_dispatch_registry; } static void register_function( Function_Type type, std::string dispatch_chars, Function f ) { switch ( type ) { case ESCAPE: get_global_dispatch_registry().escape.insert( dispatch_map_t::value_type( dispatch_chars, f ) ); break; case CSI: get_global_dispatch_registry().CSI.insert( dispatch_map_t::value_type( dispatch_chars, f ) ); break; case CONTROL: get_global_dispatch_registry().control.insert( dispatch_map_t::value_type( dispatch_chars, f ) ); break; } } Function::Function( Function_Type type, std::string dispatch_chars, void (*s_function)( Framebuffer *, Dispatcher * ), bool s_clears_wrap_state ) : function( s_function ), clears_wrap_state( s_clears_wrap_state ) { register_function( type, dispatch_chars, *this ); } void Dispatcher::dispatch( Function_Type type, const Parser::Action *act, Framebuffer *fb ) { /* add final char to dispatch key */ if ( (type == ESCAPE) || (type == CSI) ) { assert( act->char_present ); Parser::Collect act2; act2.char_present = true; act2.ch = act->ch; collect( &act2 ); } dispatch_map_t *map = NULL; switch ( type ) { case ESCAPE: map = &get_global_dispatch_registry().escape; break; case CSI: map = &get_global_dispatch_registry().CSI; break; case CONTROL: map = &get_global_dispatch_registry().control; break; } std::string key = dispatch_chars; if ( type == CONTROL ) { assert( act->ch <= 255 ); char ctrlstr[ 2 ] = { (char)act->ch, 0 }; key = std::string( ctrlstr, 1 ); } dispatch_map_t::const_iterator i = map->find( key ); if ( i == map->end() ) { /* unknown function */ fb->ds.next_print_will_wrap = false; return; } else { act->handled = true; if ( i->second.clears_wrap_state ) { fb->ds.next_print_will_wrap = false; } return i->second.function( fb, this ); } } void Dispatcher::OSC_put( const Parser::OSC_Put *act ) { assert( act->char_present ); if ( OSC_string.size() < 256 ) { /* should be a long enough window title */ OSC_string.push_back( act->ch ); act->handled = true; } } void Dispatcher::OSC_start( const Parser::OSC_Start *act ) { OSC_string.clear(); act->handled = true; } bool Dispatcher::operator==( const Dispatcher &x ) const { return ( params == x.params ) && ( parsed_params == x.parsed_params ) && ( parsed == x.parsed ) && ( dispatch_chars == x.dispatch_chars ) && ( OSC_string == x.OSC_string ) && ( terminal_to_host == x.terminal_to_host ); } mosh-1.2.5/src/terminal/terminaldispatcher.h000066400000000000000000000072021255051512600211250ustar00rootroot00000000000000/* Mosh: the mobile shell Copyright 2012 Keith Winstein This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . In addition, as a special exception, the copyright holders give permission to link the code of portions of this program with the OpenSSL library under certain conditions as described in each individual source file, and distribute linked combinations including the two. You must obey the GNU General Public License in all respects for all of the code used other than OpenSSL. If you modify file(s) with this exception, you may extend this exception to your version of the file(s), but you are not obligated to do so. If you do not wish to do so, delete this exception statement from your version. If you delete this exception statement from all source files in the program, then also delete it here. */ #ifndef TERMINALDISPATCHER_HPP #define TERMINALDISPATCHER_HPP #include #include #include namespace Parser { class Action; class Param; class Collect; class Clear; class Esc_Dispatch; class CSI_Dispatch; class Execute; class OSC_Start; class OSC_Put; class OSC_End; } namespace Terminal { class Framebuffer; class Dispatcher; enum Function_Type { ESCAPE, CSI, CONTROL }; class Function { public: Function() : function( NULL ), clears_wrap_state( true ) {} Function( Function_Type type, std::string dispatch_chars, void (*s_function)( Framebuffer *, Dispatcher * ), bool s_clears_wrap_state = true ); void (*function)( Framebuffer *, Dispatcher * ); bool clears_wrap_state; }; typedef std::map dispatch_map_t; class DispatchRegistry { public: dispatch_map_t escape; dispatch_map_t CSI; dispatch_map_t control; DispatchRegistry() : escape(), CSI(), control() {} }; DispatchRegistry & get_global_dispatch_registry( void ); class Dispatcher { private: std::string params; std::vector parsed_params; bool parsed; std::string dispatch_chars; std::vector OSC_string; /* only used to set the window title */ void parse_params( void ); public: static const int PARAM_MAX = 65535; /* prevent evil escape sequences from causing long loops */ std::string terminal_to_host; /* this is the reply string */ Dispatcher(); int getparam( size_t N, int defaultval ); int param_count( void ); void newparamchar( const Parser::Param *act ); void collect( const Parser::Collect *act ); void clear( const Parser::Clear *act ); std::string str( void ); void dispatch( Function_Type type, const Parser::Action *act, Framebuffer *fb ); std::string get_dispatch_chars( void ) const { return dispatch_chars; } std::vector get_OSC_string( void ) const { return OSC_string; } void OSC_put( const Parser::OSC_Put *act ); void OSC_start( const Parser::OSC_Start *act ); void OSC_dispatch( const Parser::OSC_End *act, Framebuffer *fb ); bool operator==( const Dispatcher &x ) const; }; } #endif mosh-1.2.5/src/terminal/terminaldisplay.cc000066400000000000000000000324641255051512600206120ustar00rootroot00000000000000/* Mosh: the mobile shell Copyright 2012 Keith Winstein This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . In addition, as a special exception, the copyright holders give permission to link the code of portions of this program with the OpenSSL library under certain conditions as described in each individual source file, and distribute linked combinations including the two. You must obey the GNU General Public License in all respects for all of the code used other than OpenSSL. If you modify file(s) with this exception, you may extend this exception to your version of the file(s), but you are not obligated to do so. If you do not wish to do so, delete this exception statement from your version. If you delete this exception statement from all source files in the program, then also delete it here. */ #include #include "terminaldisplay.h" using namespace Terminal; /* Print a new "frame" to the terminal, using ANSI/ECMA-48 escape codes. */ static const Renditions & initial_rendition( void ) { const static Renditions blank = Renditions( 0 ); return blank; } std::string Display::open() const { return std::string( smcup ? smcup : "" ) + std::string( "\033[?1h" ); } std::string Display::close() const { return std::string( "\033[?1l\033[0m\033[?25h" "\033[?1003l\033[?1002l\033[?1001l\033[?1000l" "\033[?1015l\033[?1006l\033[?1005l" ) + std::string( rmcup ? rmcup : "" ); } std::string Display::new_frame( bool initialized, const Framebuffer &last, const Framebuffer &f ) const { FrameState frame( last ); char tmp[ 64 ]; /* has bell been rung? */ if ( f.get_bell_count() != frame.last_frame.get_bell_count() ) { frame.append( "\x07" ); } /* has icon name or window title changed? */ if ( has_title && f.is_title_initialized() && ( (!initialized) || (f.get_icon_name() != frame.last_frame.get_icon_name()) || (f.get_window_title() != frame.last_frame.get_window_title()) ) ) { /* set icon name and window title */ if ( f.get_icon_name() == f.get_window_title() ) { /* write combined Icon Name and Window Title */ frame.append( "\033]0;" ); const std::deque &window_title( f.get_window_title() ); for ( std::deque::const_iterator i = window_title.begin(); i != window_title.end(); i++ ) { snprintf( tmp, 64, "%lc", (wint_t)*i ); frame.append( tmp ); } frame.append( "\007" ); /* ST is more correct, but BEL more widely supported */ } else { /* write Icon Name */ frame.append( "\033]1;" ); const std::deque &icon_name( f.get_icon_name() ); for ( std::deque::const_iterator i = icon_name.begin(); i != icon_name.end(); i++ ) { snprintf( tmp, 64, "%lc", (wint_t)*i ); frame.append( tmp ); } frame.append( "\007" ); frame.append( "\033]2;" ); const std::deque &window_title( f.get_window_title() ); for ( std::deque::const_iterator i = window_title.begin(); i != window_title.end(); i++ ) { snprintf( tmp, 64, "%lc", (wint_t)*i ); frame.append( tmp ); } frame.append( "\007" ); } } /* has reverse video state changed? */ if ( (!initialized) || (f.ds.reverse_video != frame.last_frame.ds.reverse_video) ) { /* set reverse video */ snprintf( tmp, 64, "\033[?5%c", (f.ds.reverse_video ? 'h' : 'l') ); frame.append( tmp ); } /* has size changed? */ if ( (!initialized) || (f.ds.get_width() != frame.last_frame.ds.get_width()) || (f.ds.get_height() != frame.last_frame.ds.get_height()) ) { /* reset scrolling region */ snprintf( tmp, 64, "\033[%d;%dr", 1, f.ds.get_height() ); frame.append( tmp ); /* clear screen */ frame.append( "\033[0m\033[H\033[2J" ); initialized = false; frame.cursor_x = frame.cursor_y = 0; frame.current_rendition = initial_rendition(); } else { frame.cursor_x = frame.last_frame.ds.get_cursor_col(); frame.cursor_y = frame.last_frame.ds.get_cursor_row(); frame.current_rendition = frame.last_frame.ds.get_renditions(); } /* shortcut -- has display moved up by a certain number of lines? */ frame.y = 0; if ( initialized ) { int lines_scrolled = 0; int scroll_height = 0; for ( int row = 0; row < frame.last_frame.ds.get_height(); row++ ) { if ( *(f.get_row( 0 )) == *(frame.last_frame.get_row( row )) ) { /* found a scroll */ lines_scrolled = row; scroll_height = 1; /* how big is the region that was scrolled? */ for ( int region_height = 1; lines_scrolled + region_height < f.ds.get_height(); region_height++ ) { if ( *(f.get_row( region_height )) == *(frame.last_frame.get_row( lines_scrolled + region_height )) ) { scroll_height = region_height + 1; } else { break; } } break; } } if ( scroll_height ) { frame.y = scroll_height; if ( lines_scrolled ) { if ( !(frame.current_rendition == initial_rendition()) ) { frame.append( "\033[0m" ); frame.current_rendition = initial_rendition(); } int top_margin = 0; int bottom_margin = top_margin + lines_scrolled + scroll_height - 1; assert( bottom_margin < f.ds.get_height() ); /* set scrolling region */ snprintf( tmp, 64, "\033[%d;%dr", top_margin + 1, bottom_margin + 1); frame.append( tmp ); /* go to bottom of scrolling region */ frame.append_silent_move( bottom_margin, 0 ); /* scroll */ for ( int i = 0; i < lines_scrolled; i++ ) { frame.append( "\n" ); } /* do the move in memory */ for ( int i = top_margin; i <= bottom_margin; i++ ) { if ( i + lines_scrolled <= bottom_margin ) { *(frame.last_frame.get_mutable_row( i )) = *(frame.last_frame.get_row( i + lines_scrolled )); } else { frame.last_frame.get_mutable_row( i )->reset( 0 ); } } /* reset scrolling region */ snprintf( tmp, 64, "\033[%d;%dr", 1, f.ds.get_height() ); frame.append( tmp ); /* invalidate cursor position after unsetting scrolling region */ frame.cursor_x = frame.cursor_y = -1; } } } /* iterate for every cell */ for ( ; frame.y < f.ds.get_height(); frame.y++ ) { int last_x = 0; for ( frame.x = 0; frame.x < f.ds.get_width(); /* let put_cell() handle advance */ ) { last_x = frame.x; put_cell( initialized, frame, f ); } /* To hint that a word-select should group the end of one line with the beginning of the next, we let the real cursor actually wrap around in cases where it wrapped around for us. */ if ( (frame.y < f.ds.get_height() - 1) && f.get_row( frame.y )->get_wrap() ) { frame.x = last_x; while ( frame.x < f.ds.get_width() ) { frame.force_next_put = true; put_cell( initialized, frame, f ); } /* next write will wrap */ frame.cursor_x = 0; frame.cursor_y++; frame.force_next_put = true; } /* Turn off wrap */ if ( (frame.y < f.ds.get_height() - 1) && (!f.get_row( frame.y )->get_wrap()) && (!initialized || frame.last_frame.get_row( frame.y )->get_wrap()) ) { frame.x = last_x; if ( initialized ) { frame.last_frame.reset_cell( frame.last_frame.get_mutable_cell( frame.y, frame.x ) ); } snprintf( tmp, 64, "\033[%d;%dH\033[K", frame.y + 1, frame.x + 1 ); frame.append( tmp ); frame.cursor_x = frame.x; frame.force_next_put = true; put_cell( initialized, frame, f ); } } /* has cursor location changed? */ if ( (!initialized) || (f.ds.get_cursor_row() != frame.cursor_y) || (f.ds.get_cursor_col() != frame.cursor_x) ) { snprintf( tmp, 64, "\033[%d;%dH", f.ds.get_cursor_row() + 1, f.ds.get_cursor_col() + 1 ); frame.append( tmp ); frame.cursor_x = f.ds.get_cursor_col(); frame.cursor_y = f.ds.get_cursor_row(); } /* has cursor visibility changed? */ if ( (!initialized) || (f.ds.cursor_visible != frame.last_frame.ds.cursor_visible) ) { if ( f.ds.cursor_visible ) { frame.append( "\033[?25h" ); } else { frame.append( "\033[?25l" ); } } /* have renditions changed? */ if ( (!initialized) || !(f.ds.get_renditions() == frame.current_rendition) ) { frame.appendstring( f.ds.get_renditions().sgr() ); frame.current_rendition = f.ds.get_renditions(); } /* has bracketed paste mode changed? */ if ( (!initialized) || (f.ds.bracketed_paste != frame.last_frame.ds.bracketed_paste) ) { frame.append( f.ds.bracketed_paste ? "\033[?2004h" : "\033[?2004l" ); } /* has mouse reporting mode changed? */ if ( (!initialized) || (f.ds.mouse_reporting_mode != frame.last_frame.ds.mouse_reporting_mode) ) { if (f.ds.mouse_reporting_mode == DrawState::MOUSE_REPORTING_NONE) { frame.append("\033[?1003l"); frame.append("\033[?1002l"); frame.append("\033[?1001l"); frame.append("\033[?1000l"); } else { if (frame.last_frame.ds.mouse_reporting_mode != DrawState::MOUSE_REPORTING_NONE) { snprintf(tmp, sizeof(tmp), "\033[?%dl", frame.last_frame.ds.mouse_reporting_mode); frame.append(tmp); } snprintf(tmp, sizeof(tmp), "\033[?%dh", f.ds.mouse_reporting_mode); frame.append(tmp); } } /* has mouse focus mode changed? */ if ( (!initialized) || (f.ds.mouse_focus_event != frame.last_frame.ds.mouse_focus_event) ) { frame.append( f.ds.mouse_focus_event ? "\033[?1004h" : "\033[?1004l" ); } /* has mouse encoding mode changed? */ if ( (!initialized) || (f.ds.mouse_encoding_mode != frame.last_frame.ds.mouse_encoding_mode) ) { if (f.ds.mouse_encoding_mode == DrawState::MOUSE_ENCODING_DEFAULT) { frame.append("\033[?1015l"); frame.append("\033[?1006l"); frame.append("\033[?1005l"); } else { if (frame.last_frame.ds.mouse_encoding_mode != DrawState::MOUSE_ENCODING_DEFAULT) { snprintf(tmp, sizeof(tmp), "\033[?%dl", frame.last_frame.ds.mouse_encoding_mode); frame.append(tmp); } snprintf(tmp, sizeof(tmp), "\033[?%dh", f.ds.mouse_encoding_mode); frame.append(tmp); } } return frame.str; } void Display::put_cell( bool initialized, FrameState &frame, const Framebuffer &f ) const { char tmp[ 64 ]; const Cell *cell = f.get_cell( frame.y, frame.x ); if ( !frame.force_next_put ) { if ( initialized && ( *cell == *(frame.last_frame.get_cell( frame.y, frame.x )) ) ) { frame.x += cell->width; return; } } if ( (frame.x != frame.cursor_x) || (frame.y != frame.cursor_y) ) { frame.append_silent_move( frame.y, frame.x ); } if ( !(frame.current_rendition == cell->renditions) ) { /* print renditions */ frame.appendstring( cell->renditions.sgr() ); frame.current_rendition = cell->renditions; } if ( cell->contents.empty() ) { /* see how far we can stretch a clear */ int clear_count = 0; for ( int col = frame.x; col < f.ds.get_width(); col++ ) { const Cell *other_cell = f.get_cell( frame.y, col ); if ( (cell->renditions == other_cell->renditions) && (other_cell->contents.empty()) ) { clear_count++; } else { break; } } assert( frame.x + clear_count <= f.ds.get_width() ); bool can_use_erase = has_bce || (cell->renditions == initial_rendition()); if ( frame.force_next_put ) { frame.append( " " ); frame.cursor_x++; frame.x++; frame.force_next_put = false; return; } /* can we go to the end of the line? */ if ( (frame.x + clear_count == f.ds.get_width()) && can_use_erase ) { frame.append( "\033[K" ); frame.x += clear_count; } else { if ( has_ech && can_use_erase ) { if ( clear_count == 1 ) { frame.append( "\033[X" ); } else { snprintf( tmp, 64, "\033[%dX", clear_count ); frame.append( tmp ); } frame.x += clear_count; } else { /* no ECH, so just print a space */ /* unlike erases, this will use background color irrespective of BCE */ frame.append( " " ); frame.cursor_x++; frame.x++; } } return; } /* cells that begin with combining character get combiner attached to no-break space */ if ( cell->fallback ) { frame.append( "\xC2\xA0" ); } for ( std::vector::const_iterator i = cell->contents.begin(); i != cell->contents.end(); i++ ) { snprintf( tmp, 64, "%lc", (wint_t)*i ); frame.append( tmp ); } frame.x += cell->width; frame.cursor_x += cell->width; frame.force_next_put = false; } void FrameState::append_silent_move( int y, int x ) { char tmp[ 64 ]; /* turn off cursor if necessary before moving cursor */ if ( last_frame.ds.cursor_visible ) { append( "\033[?25l" ); last_frame.ds.cursor_visible = false; } snprintf( tmp, 64, "\033[%d;%dH", y + 1, x + 1 ); append( tmp ); cursor_x = x; cursor_y = y; } mosh-1.2.5/src/terminal/terminaldisplay.h000066400000000000000000000062131255051512600204450ustar00rootroot00000000000000/* Mosh: the mobile shell Copyright 2012 Keith Winstein This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . In addition, as a special exception, the copyright holders give permission to link the code of portions of this program with the OpenSSL library under certain conditions as described in each individual source file, and distribute linked combinations including the two. You must obey the GNU General Public License in all respects for all of the code used other than OpenSSL. If you modify file(s) with this exception, you may extend this exception to your version of the file(s), but you are not obligated to do so. If you do not wish to do so, delete this exception statement from your version. If you delete this exception statement from all source files in the program, then also delete it here. */ #ifndef TERMINALDISPLAY_HPP #define TERMINALDISPLAY_HPP #include "terminalframebuffer.h" namespace Terminal { /* variables used within a new_frame */ class FrameState { public: int x, y; bool force_next_put; std::string str; int cursor_x, cursor_y; Renditions current_rendition; Framebuffer last_frame; FrameState( const Framebuffer &s_last ) : x(0), y(0), force_next_put( false ), str(), cursor_x(0), cursor_y(0), current_rendition( 0 ), last_frame( s_last ) { str.reserve( 1024 ); } void append( const char * s ) { str.append( s ); } void appendstring( const std::string &s ) { str.append( s ); } void append_silent_move( int y, int x ); }; class Display { private: static bool ti_flag( const char *capname ); static int ti_num( const char *capname ); static const char *ti_str( const char *capname ); bool has_ech; /* erase character is part of vt200 but not supported by tmux (or by "screen" terminfo entry, which is what tmux advertises) */ bool has_bce; /* erases result in cell filled with background color */ bool has_title; /* supports window title and icon name */ int posterize_colors; /* downsample input colors >8 to [0..7] */ const char *smcup, *rmcup; /* enter and exit alternate screen mode */ void put_cell( bool initialized, FrameState &frame, const Framebuffer &f ) const; public: void downgrade( Framebuffer &f ) const { if ( posterize_colors ) { f.posterize(); } } std::string open() const; std::string close() const; std::string new_frame( bool initialized, const Framebuffer &last, const Framebuffer &f ) const; Display( bool use_environment ); }; } #endif mosh-1.2.5/src/terminal/terminaldisplayinit.cc000066400000000000000000000114131255051512600214650ustar00rootroot00000000000000/* Mosh: the mobile shell Copyright 2012 Keith Winstein This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . In addition, as a special exception, the copyright holders give permission to link the code of portions of this program with the OpenSSL library under certain conditions as described in each individual source file, and distribute linked combinations including the two. You must obey the GNU General Public License in all respects for all of the code used other than OpenSSL. If you modify file(s) with this exception, you may extend this exception to your version of the file(s), but you are not obligated to do so. If you do not wish to do so, delete this exception statement from your version. If you delete this exception statement from all source files in the program, then also delete it here. */ /* This is in its own file because otherwise the ncurses #defines alias our own variable names. */ #include "config.h" #include "terminaldisplay.h" #include #include #if defined HAVE_NCURSESW_CURSES_H # include # include #elif defined HAVE_NCURSESW_H # include # include #elif defined HAVE_NCURSES_CURSES_H # include # include #elif defined HAVE_NCURSES_H # include # include #elif defined HAVE_CURSES_H # include # include #else # error "SysV or X/Open-compatible Curses header file required" #endif #include #include using namespace Terminal; bool Display::ti_flag( const char *capname ) { int val = tigetflag( const_cast( capname ) ); if ( val == -1 ) { throw std::invalid_argument( std::string( "Invalid terminfo boolean capability " ) + capname ); } return val; } int Display::ti_num( const char *capname ) { int val = tigetnum( const_cast( capname ) ); if ( val == -2 ) { throw std::invalid_argument( std::string( "Invalid terminfo numeric capability " ) + capname ); } return val; } const char *Display::ti_str( const char *capname ) { const char *val = tigetstr( const_cast( capname ) ); if ( val == (const char *)-1 ) { throw std::invalid_argument( std::string( "Invalid terminfo string capability " ) + capname ); } return val; } Display::Display( bool use_environment ) : has_ech( true ), has_bce( true ), has_title( true ), posterize_colors( false ), smcup( NULL ), rmcup( NULL ) { if ( use_environment ) { int errret = -2; int ret = setupterm( (char *)0, 1, &errret ); if ( ret != OK ) { switch ( errret ) { case 1: throw std::runtime_error( "Terminal is hardcopy and cannot be used by curses applications." ); break; case 0: throw std::runtime_error( "Unknown terminal type." ); break; case -1: throw std::runtime_error( "Terminfo database could not be found." ); break; default: throw std::runtime_error( "Unknown terminfo error." ); break; } } /* check for ECH */ has_ech = ti_str( "ech" ); /* check for BCE */ has_bce = ti_flag( "bce" ); /* Check if we can set the window title and icon name. terminfo does not have reliable information on this, so we hardcode a whitelist of terminal type prefixes. This is the list from Debian's default screenrc, plus "screen" itself (which also covers tmux). */ static const char * const title_term_types[] = { "xterm", "rxvt", "kterm", "Eterm", "screen" }; has_title = false; const char *term_type = getenv( "TERM" ); if ( term_type ) { for ( size_t i = 0; i < sizeof( title_term_types ) / sizeof( const char * ); i++ ) { if ( 0 == strncmp( term_type, title_term_types[ i ], strlen( title_term_types[ i ] ) ) ) { has_title = true; break; } } } /* posterization disabled because server now only advertises xterm-256color when client has colors = 256 */ /* posterize_colors = ti_num( "colors" ) < 256; */ if ( !getenv( "MOSH_NO_TERM_INIT" ) ) { smcup = ti_str("smcup"); rmcup = ti_str("rmcup"); } } } mosh-1.2.5/src/terminal/terminalframebuffer.cc000066400000000000000000000411731255051512600214260ustar00rootroot00000000000000/* Mosh: the mobile shell Copyright 2012 Keith Winstein This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . In addition, as a special exception, the copyright holders give permission to link the code of portions of this program with the OpenSSL library under certain conditions as described in each individual source file, and distribute linked combinations including the two. You must obey the GNU General Public License in all respects for all of the code used other than OpenSSL. If you modify file(s) with this exception, you may extend this exception to your version of the file(s), but you are not obligated to do so. If you do not wish to do so, delete this exception statement from your version. If you delete this exception statement from all source files in the program, then also delete it here. */ #include #include #include "terminalframebuffer.h" using namespace Terminal; void Cell::reset( int background_color ) { contents.clear(); fallback = false; width = 1; renditions = Renditions( background_color ); wrap = false; } void DrawState::reinitialize_tabs( unsigned int start ) { assert( default_tabs ); for ( unsigned int i = start; i < tabs.size(); i++ ) { tabs[ i ] = ( (i % 8) == 0 ); } } DrawState::DrawState( int s_width, int s_height ) : width( s_width ), height( s_height ), cursor_col( 0 ), cursor_row( 0 ), combining_char_col( 0 ), combining_char_row( 0 ), default_tabs( true ), tabs( s_width ), scrolling_region_top_row( 0 ), scrolling_region_bottom_row( height - 1 ), renditions( 0 ), save(), next_print_will_wrap( false ), origin_mode( false ), auto_wrap_mode( true ), insert_mode( false ), cursor_visible( true ), reverse_video( false ), bracketed_paste( false ), mouse_reporting_mode( MOUSE_REPORTING_NONE ), mouse_focus_event( false ), mouse_alternate_scroll( false ), mouse_encoding_mode( MOUSE_ENCODING_DEFAULT ), application_mode_cursor_keys( false ) { reinitialize_tabs( 0 ); } Framebuffer::Framebuffer( int s_width, int s_height ) : rows( s_height, Row( s_width, 0 ) ), icon_name(), window_title(), bell_count( 0 ), title_initialized( false ), ds( s_width, s_height ) { assert( s_height > 0 ); assert( s_width > 0 ); } void Framebuffer::scroll( int N ) { if ( N >= 0 ) { for ( int i = 0; i < N; i++ ) { delete_line( ds.get_scrolling_region_top_row() ); ds.move_row( -1, true ); } } else { N = -N; for ( int i = 0; i < N; i++ ) { rows.insert( rows.begin() + ds.get_scrolling_region_top_row(), newrow() ); rows.erase( rows.begin() + ds.get_scrolling_region_bottom_row() + 1 ); ds.move_row( 1, true ); } } } void DrawState::new_grapheme( void ) { combining_char_col = cursor_col; combining_char_row = cursor_row; } void DrawState::snap_cursor_to_border( void ) { if ( cursor_row < limit_top() ) cursor_row = limit_top(); if ( cursor_row > limit_bottom() ) cursor_row = limit_bottom(); if ( cursor_col < 0 ) cursor_col = 0; if ( cursor_col >= width ) cursor_col = width - 1; } void DrawState::move_row( int N, bool relative ) { if ( relative ) { cursor_row += N; } else { cursor_row = N + limit_top(); } snap_cursor_to_border(); new_grapheme(); next_print_will_wrap = false; } void DrawState::move_col( int N, bool relative, bool implicit ) { if ( implicit ) { new_grapheme(); } if ( relative ) { cursor_col += N; } else { cursor_col = N; } if ( implicit ) { next_print_will_wrap = (cursor_col >= width); } snap_cursor_to_border(); if ( !implicit ) { new_grapheme(); next_print_will_wrap = false; } } void Framebuffer::move_rows_autoscroll( int rows ) { /* don't scroll if outside the scrolling region */ if ( (ds.get_cursor_row() < ds.get_scrolling_region_top_row()) || (ds.get_cursor_row() > ds.get_scrolling_region_bottom_row()) ) { ds.move_row( rows, true ); return; } if ( ds.get_cursor_row() + rows > ds.get_scrolling_region_bottom_row() ) { scroll( ds.get_cursor_row() + rows - ds.get_scrolling_region_bottom_row() ); } else if ( ds.get_cursor_row() + rows < ds.get_scrolling_region_top_row() ) { scroll( ds.get_cursor_row() + rows - ds.get_scrolling_region_top_row() ); } ds.move_row( rows, true ); } Cell *Framebuffer::get_combining_cell( void ) { if ( (ds.get_combining_char_col() < 0) || (ds.get_combining_char_row() < 0) || (ds.get_combining_char_col() >= ds.get_width()) || (ds.get_combining_char_row() >= ds.get_height()) ) { return NULL; } /* can happen if a resize came in between */ return &rows[ ds.get_combining_char_row() ].cells[ ds.get_combining_char_col() ]; } void DrawState::set_tab( void ) { tabs[ cursor_col ] = true; } void DrawState::clear_tab( int col ) { tabs[ col ] = false; } int DrawState::get_next_tab( void ) const { for ( int i = cursor_col + 1; i < width; i++ ) { if ( tabs[ i ] ) { return i; } } return -1; } void DrawState::set_scrolling_region( int top, int bottom ) { if ( height < 1 ) { return; } scrolling_region_top_row = top; scrolling_region_bottom_row = bottom; if ( scrolling_region_top_row < 0 ) scrolling_region_top_row = 0; if ( scrolling_region_bottom_row >= height ) scrolling_region_bottom_row = height - 1; if ( scrolling_region_bottom_row < scrolling_region_top_row ) scrolling_region_bottom_row = scrolling_region_top_row; /* real rule requires TWO-line scrolling region */ if ( origin_mode ) { snap_cursor_to_border(); new_grapheme(); } } int DrawState::limit_top( void ) const { return origin_mode ? scrolling_region_top_row : 0; } int DrawState::limit_bottom( void ) const { return origin_mode ? scrolling_region_bottom_row : height - 1; } void Framebuffer::apply_renditions_to_current_cell( void ) { get_mutable_cell()->renditions = ds.get_renditions(); } SavedCursor::SavedCursor() : cursor_col( 0 ), cursor_row( 0 ), renditions( 0 ), auto_wrap_mode( true ), origin_mode( false ) {} void DrawState::save_cursor( void ) { save.cursor_col = cursor_col; save.cursor_row = cursor_row; save.renditions = renditions; save.auto_wrap_mode = auto_wrap_mode; save.origin_mode = origin_mode; } void DrawState::restore_cursor( void ) { cursor_col = save.cursor_col; cursor_row = save.cursor_row; renditions = save.renditions; auto_wrap_mode = save.auto_wrap_mode; origin_mode = save.origin_mode; snap_cursor_to_border(); /* we could have resized in between */ new_grapheme(); } void Framebuffer::insert_line( int before_row ) { if ( (before_row < ds.get_scrolling_region_top_row()) || (before_row > ds.get_scrolling_region_bottom_row() + 1) ) { return; } rows.insert( rows.begin() + before_row, newrow() ); rows.erase( rows.begin() + ds.get_scrolling_region_bottom_row() + 1 ); } void Framebuffer::delete_line( int row ) { if ( (row < ds.get_scrolling_region_top_row()) || (row > ds.get_scrolling_region_bottom_row()) ) { return; } int insertbefore = ds.get_scrolling_region_bottom_row() + 1; if ( insertbefore == ds.get_height() ) { rows.push_back( newrow() ); } else { rows.insert( rows.begin() + insertbefore, newrow() ); } rows.erase( rows.begin() + row ); } void Row::insert_cell( int col, int background_color ) { cells.insert( cells.begin() + col, Cell( background_color ) ); cells.pop_back(); } void Row::delete_cell( int col, int background_color ) { cells.push_back( Cell( background_color ) ); cells.erase( cells.begin() + col ); } void Framebuffer::insert_cell( int row, int col ) { rows[ row ].insert_cell( col, ds.get_background_rendition() ); } void Framebuffer::delete_cell( int row, int col ) { rows[ row ].delete_cell( col, ds.get_background_rendition() ); } void Framebuffer::reset( void ) { int width = ds.get_width(), height = ds.get_height(); ds = DrawState( width, height ); rows = std::deque( height, newrow() ); window_title.clear(); /* do not reset bell_count */ } void Framebuffer::soft_reset( void ) { ds.insert_mode = false; ds.origin_mode = false; ds.cursor_visible = true; /* per xterm and gnome-terminal */ ds.application_mode_cursor_keys = false; ds.set_scrolling_region( 0, ds.get_height() - 1 ); ds.add_rendition( 0 ); ds.clear_saved_cursor(); } void Framebuffer::posterize( void ) { for ( rows_type::iterator i = rows.begin(); i != rows.end(); i++ ) { for ( Row::cells_type::iterator j = i->cells.begin(); j != i->cells.end(); j++ ) { j->renditions.posterize(); } } } void Framebuffer::resize( int s_width, int s_height ) { assert( s_width > 0 ); assert( s_height > 0 ); rows.resize( s_height, newrow() ); for ( rows_type::iterator i = rows.begin(); i != rows.end(); i++ ) { i->set_wrap( false ); i->cells.resize( s_width, Cell( ds.get_background_rendition() ) ); } ds.resize( s_width, s_height ); } void DrawState::resize( int s_width, int s_height ) { if ( (width != s_width) || (height != s_height) ) { /* reset entire scrolling region on any resize */ /* xterm and rxvt-unicode do this. gnome-terminal only resets scrolling region if it has to become smaller in resize */ scrolling_region_top_row = 0; scrolling_region_bottom_row = s_height - 1; } tabs.resize( s_width ); if ( default_tabs ) { reinitialize_tabs( width ); } width = s_width; height = s_height; snap_cursor_to_border(); /* saved cursor will be snapped to border on restore */ /* invalidate combining char cell if necessary */ if ( (combining_char_col >= width) || (combining_char_row >= height) ) { combining_char_col = combining_char_row = -1; } } Renditions::Renditions( int s_background ) : bold( false ), italic( false ), underlined( false ), blink( false ), inverse( false ), invisible( false ), foreground_color( 0 ), background_color( s_background ) {} /* This routine cannot be used to set a color beyond the 16-color set. */ void Renditions::set_rendition( int num ) { if ( num == 0 ) { bold = italic = underlined = blink = inverse = invisible = false; foreground_color = background_color = 0; return; } if ( num == 39 ) { foreground_color = 0; return; } else if ( num == 49 ) { background_color = 0; return; } if ( (30 <= num) && (num <= 37) ) { /* foreground color in 8-color set */ foreground_color = num; return; } else if ( (40 <= num) && (num <= 47) ) { /* background color in 8-color set */ background_color = num; return; } else if ( (90 <= num) && (num <= 97) ) { /* foreground color in 16-color set */ foreground_color = num - 90 + 38; return; } else if ( (100 <= num) && (num <= 107) ) { /* background color in 16-color set */ background_color = num - 100 + 48; return; } switch ( num ) { case 1: case 22: bold = (num == 1); break; case 3: case 23: italic = (num == 3); break; case 4: case 24: underlined = (num == 4); break; case 5: case 25: blink = (num == 5); break; case 7: case 27: inverse = (num == 7); break; case 8: case 28: invisible = (num == 8); break; } } void Renditions::set_foreground_color( int num ) { if ( (0 <= num) && (num <= 255) ) { foreground_color = 30 + num; } } void Renditions::set_background_color( int num ) { if ( (0 <= num) && (num <= 255) ) { background_color = 40 + num; } } std::string Renditions::sgr( void ) const { std::string ret; ret.append( "\033[0" ); if ( bold ) ret.append( ";1" ); if ( italic ) ret.append( ";3" ); if ( underlined ) ret.append( ";4" ); if ( blink ) ret.append( ";5" ); if ( inverse ) ret.append( ";7" ); if ( invisible ) ret.append( ";8" ); if ( foreground_color && (foreground_color <= 37) ) { /* ANSI foreground color */ char col[ 8 ]; snprintf( col, 8, ";%d", foreground_color ); ret.append( col ); } if ( background_color && (background_color <= 47) ) { char col[ 8 ]; snprintf( col, 8, ";%d", background_color ); ret.append( col ); } ret.append( "m" ); if ( foreground_color > 37 ) { /* use 256-color set */ char col[ 64 ]; snprintf( col, 64, "\033[38;5;%dm", foreground_color - 30 ); ret.append( col ); } if ( background_color > 47 ) { /* use 256-color set */ char col[ 64 ]; snprintf( col, 64, "\033[48;5;%dm", background_color - 40 ); ret.append( col ); } return ret; } /* Reduce 256 "standard" colors to the 8 ANSI colors. */ /* Terminal emulators generally agree on the (R',G',B') values of the "standard" 256-color palette beyond #15, but for the first 16 colors there is disagreement. Most terminal emulators are roughly self-consistent, except on Ubuntu's gnome-terminal where "ANSI blue" (#4) has been replaced with the aubergine system-wide color. See https://lists.ubuntu.com/archives/ubuntu-devel/2011-March/032726.html Terminal emulators that advertise "xterm" are inconsistent on the handling of initc to change the contents of a cell in the color palette. On RIS (reset to initial state) or choosing reset from the user interface, xterm resets all entries, but gnome-terminal only resets entries beyond 16. (rxvt doesn't reset any entries, and Terminal.app ignores initc.) On initc, xterm applies changes immediately (but slowly), but gnome-terminal's changes are only prospective unless the user resizes the terminal. mosh ignores initc for now, despite advertising xterm-256color. */ /* Table mapping common color cube for [16 .. 255] to xterm's system colors (0 .. 7) with closest CIE deltaE(2000). */ static const char standard_posterization[] = { 0, 1, 2, 3, 4, 5, 6, 7, 7, 1, 2, 3, 4, 5, 6, 7, 0, 4, 4, 4, 4, 4, 0, 0, 4, 4, 4, 4, 2, 6, 6, 6, 6, 4, 2, 2, 6, 6, 6, 6, 2, 2, 2, 6, 6, 6, 2, 2, 2, 2, 6, 6, 1, 4, 4, 4, 4, 4, 0, 0, 4, 4, 4, 4, 2, 2, 6, 6, 4, 5, 2, 2, 6, 6, 6, 6, 2, 2, 2, 6, 6, 6, 2, 2, 2, 2, 6, 6, 1, 5, 5, 4, 4, 4, 1, 1, 5, 5, 5, 5, 3, 3, 7, 5, 5, 5, 2, 2, 2, 6, 7, 7, 2, 2, 2, 6, 6, 6, 2, 2, 2, 2, 6, 6, 1, 5, 5, 5, 5, 5, 1, 1, 5, 5, 5, 5, 3, 1, 7, 5, 5, 5, 3, 3, 3, 7, 7, 7, 3, 3, 2, 7, 6, 7, 3, 2, 2, 2, 6, 6, 1, 5, 5, 5, 5, 5, 1, 1, 5, 5, 5, 5, 3, 1, 1, 5, 5, 5, 3, 3, 7, 7, 7, 7, 3, 3, 3, 7, 7, 7, 3, 3, 3, 3, 7, 7, 1, 1, 5, 5, 5, 5, 1, 1, 5, 5, 5, 5, 1, 1, 1, 5, 5, 5, 3, 7, 7, 7, 7, 7, 3, 3, 3, 7, 7, 7, 3, 3, 3, 3, 7, 7, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7 }; void Renditions::posterize( void ) { if ( foreground_color ) { foreground_color = 30 + standard_posterization[ foreground_color - 30 ]; } if ( background_color ) { background_color = 40 + standard_posterization[ background_color - 40 ]; } } void Row::reset( int background_color ) { for ( cells_type::iterator i = cells.begin(); i != cells.end(); i++ ) { i->reset( background_color ); } } void Framebuffer::prefix_window_title( const std::deque &s ) { if ( icon_name == window_title ) { /* preserve equivalence */ for ( std::deque::const_reverse_iterator i = s.rbegin(); i != s.rend(); i++ ) { icon_name.push_front( *i ); } } for ( std::deque::const_reverse_iterator i = s.rbegin(); i != s.rend(); i++ ) { window_title.push_front( *i ); } } wint_t Cell::debug_contents( void ) const { if ( contents.empty() ) { return '_'; } else { return contents.front(); } } bool Cell::compare( const Cell &other ) const { bool ret = false; if ( !contents_match( other ) ) { ret = true; fprintf( stderr, "Contents: %lc vs. %lc\n", debug_contents(), other.debug_contents() ); } if ( fallback != other.fallback ) { ret = true; fprintf( stderr, "fallback: %d vs. %d\n", fallback, other.fallback ); } if ( width != other.width ) { ret = true; fprintf( stderr, "width: %d vs. %d\n", width, other.width ); } if ( !(renditions == other.renditions) ) { ret = true; fprintf( stderr, "renditions differ\n" ); } if ( wrap != other.wrap ) { ret = true; fprintf( stderr, "wrap: %d vs. %d\n", wrap, other.wrap ); } return ret; } mosh-1.2.5/src/terminal/terminalframebuffer.h000066400000000000000000000252041255051512600212650ustar00rootroot00000000000000/* Mosh: the mobile shell Copyright 2012 Keith Winstein This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . In addition, as a special exception, the copyright holders give permission to link the code of portions of this program with the OpenSSL library under certain conditions as described in each individual source file, and distribute linked combinations including the two. You must obey the GNU General Public License in all respects for all of the code used other than OpenSSL. If you modify file(s) with this exception, you may extend this exception to your version of the file(s), but you are not obligated to do so. If you do not wish to do so, delete this exception statement from your version. If you delete this exception statement from all source files in the program, then also delete it here. */ #ifndef TERMINALFB_HPP #define TERMINALFB_HPP #include #include #include #include #include /* Terminal framebuffer */ namespace Terminal { class Renditions { public: bool bold, italic, underlined, blink, inverse, invisible; int foreground_color; int background_color; Renditions( int s_background ); void set_foreground_color( int num ); void set_background_color( int num ); void set_rendition( int num ); std::string sgr( void ) const; void posterize( void ); bool operator==( const Renditions &x ) const { return (bold == x.bold) && (italic == x.italic) && (underlined == x.underlined) && (blink == x.blink) && (inverse == x.inverse) && (invisible == x.invisible) && (foreground_color == x.foreground_color) && (background_color == x.background_color); } }; class Cell { public: std::vector contents; char fallback; /* first character is combining character */ int width; Renditions renditions; bool wrap; /* if last cell, wrap to next line */ Cell( int background_color ) : contents(), fallback( false ), width( 1 ), renditions( background_color ), wrap( false ) {} Cell() /* default constructor required by C++11 STL */ : contents(), fallback( false ), width( 1 ), renditions( 0 ), wrap( false ) { assert( false ); } void reset( int background_color ); bool operator==( const Cell &x ) const { return ( (contents == x.contents) && (fallback == x.fallback) && (width == x.width) && (renditions == x.renditions) && (wrap == x.wrap) ); } wint_t debug_contents( void ) const; bool is_blank( void ) const { return ( contents.empty() || ( (contents.size() == 1) && ( (contents.front() == 0x20) || (contents.front() == 0xA0) ) ) ); } bool contents_match ( const Cell &other ) const { return ( is_blank() && other.is_blank() ) || ( contents == other.contents ); } bool compare( const Cell &other ) const; }; class Row { public: typedef std::vector cells_type; cells_type cells; Row( size_t s_width, int background_color ) : cells( s_width, Cell( background_color ) ) {} Row() /* default constructor required by C++11 STL */ : cells( 1, Cell() ) { assert( false ); } void insert_cell( int col, int background_color ); void delete_cell( int col, int background_color ); void reset( int background_color ); bool operator==( const Row &x ) const { return ( cells == x.cells ); } bool get_wrap( void ) const { return cells.back().wrap; } void set_wrap( bool w ) { cells.back().wrap = w; } }; class SavedCursor { public: int cursor_col, cursor_row; Renditions renditions; /* not implemented: character set shift state */ bool auto_wrap_mode; bool origin_mode; /* not implemented: state of selective erase */ SavedCursor(); }; class DrawState { private: int width, height; void new_grapheme( void ); void snap_cursor_to_border( void ); int cursor_col, cursor_row; int combining_char_col, combining_char_row; bool default_tabs; std::vector tabs; void reinitialize_tabs( unsigned int start ); int scrolling_region_top_row, scrolling_region_bottom_row; Renditions renditions; SavedCursor save; public: bool next_print_will_wrap; bool origin_mode; bool auto_wrap_mode; bool insert_mode; bool cursor_visible; bool reverse_video; bool bracketed_paste; enum MouseReportingMode { MOUSE_REPORTING_NONE = 0, MOUSE_REPORTING_X10 = 9, MOUSE_REPORTING_VT220 = 1000, MOUSE_REPORTING_VT220_HILIGHT = 1001, MOUSE_REPORTING_BTN_EVENT = 1002, MOUSE_REPORTING_ANY_EVENT = 1003 } mouse_reporting_mode; bool mouse_focus_event; // 1004 bool mouse_alternate_scroll; // 1007 enum MouseEncodingMode { MOUSE_ENCODING_DEFAULT = 0, MOUSE_ENCODING_UTF8 = 1005, MOUSE_ENCODING_SGR = 1006, MOUSE_ENCODING_URXVT = 1015 } mouse_encoding_mode; bool application_mode_cursor_keys; /* bold, etc. */ void move_row( int N, bool relative = false ); void move_col( int N, bool relative = false, bool implicit = false ); int get_cursor_col( void ) const { return cursor_col; } int get_cursor_row( void ) const { return cursor_row; } int get_combining_char_col( void ) const { return combining_char_col; } int get_combining_char_row( void ) const { return combining_char_row; } int get_width( void ) const { return width; } int get_height( void ) const { return height; } void set_tab( void ); void clear_tab( int col ); void clear_default_tabs( void ) { default_tabs = false; } /* Default tabs can't be restored without resetting the draw state. */ int get_next_tab( void ) const; void set_scrolling_region( int top, int bottom ); int get_scrolling_region_top_row( void ) const { return scrolling_region_top_row; } int get_scrolling_region_bottom_row( void ) const { return scrolling_region_bottom_row; } int limit_top( void ) const; int limit_bottom( void ) const; void set_foreground_color( int x ) { renditions.set_foreground_color( x ); } void set_background_color( int x ) { renditions.set_background_color( x ); } void add_rendition( int x ) { renditions.set_rendition( x ); } Renditions get_renditions( void ) const { return renditions; } int get_background_rendition( void ) const { return renditions.background_color; } void save_cursor( void ); void restore_cursor( void ); void clear_saved_cursor( void ) { save = SavedCursor(); } void resize( int s_width, int s_height ); DrawState( int s_width, int s_height ); bool operator==( const DrawState &x ) const { /* only compare fields that affect display */ return ( width == x.width ) && ( height == x.height ) && ( cursor_col == x.cursor_col ) && ( cursor_row == x.cursor_row ) && ( cursor_visible == x.cursor_visible ) && ( reverse_video == x.reverse_video ) && ( renditions == x.renditions ) && ( bracketed_paste == x.bracketed_paste ) && ( mouse_reporting_mode == x.mouse_reporting_mode ) && ( mouse_focus_event == x.mouse_focus_event ) && ( mouse_alternate_scroll == x.mouse_alternate_scroll) && ( mouse_encoding_mode == x.mouse_encoding_mode ); } }; class Framebuffer { private: typedef std::deque rows_type; rows_type rows; std::deque icon_name; std::deque window_title; unsigned int bell_count; bool title_initialized; /* true if the window title has been set via an OSC */ Row newrow( void ) { return Row( ds.get_width(), ds.get_background_rendition() ); } public: Framebuffer( int s_width, int s_height ); DrawState ds; void scroll( int N ); void move_rows_autoscroll( int rows ); const Row *get_row( int row ) const { if ( row == -1 ) row = ds.get_cursor_row(); return &rows[ row ]; } inline const Cell *get_cell( void ) const { return &rows[ ds.get_cursor_row() ].cells[ ds.get_cursor_col() ]; } inline const Cell *get_cell( int row, int col ) const { if ( row == -1 ) row = ds.get_cursor_row(); if ( col == -1 ) col = ds.get_cursor_col(); return &rows[ row ].cells[ col ]; } Row *get_mutable_row( int row ) { if ( row == -1 ) row = ds.get_cursor_row(); return &rows[ row ]; } inline Cell *get_mutable_cell( void ) { return &rows[ ds.get_cursor_row() ].cells[ ds.get_cursor_col() ]; } inline Cell *get_mutable_cell( int row, int col ) { if ( row == -1 ) row = ds.get_cursor_row(); if ( col == -1 ) col = ds.get_cursor_col(); return &rows[ row ].cells[ col ]; } Cell *get_combining_cell( void ); void apply_renditions_to_current_cell( void ); void insert_line( int before_row ); void delete_line( int row ); void insert_cell( int row, int col ); void delete_cell( int row, int col ); void reset( void ); void soft_reset( void ); void set_title_initialized( void ) { title_initialized = true; } bool is_title_initialized( void ) const { return title_initialized; } void set_icon_name( const std::deque &s ) { icon_name = s; } void set_window_title( const std::deque &s ) { window_title = s; } const std::deque & get_icon_name( void ) const { return icon_name; } const std::deque & get_window_title( void ) const { return window_title; } void prefix_window_title( const std::deque &s ); void resize( int s_width, int s_height ); void reset_cell( Cell *c ) { c->reset( ds.get_background_rendition() ); } void reset_row( Row *r ) { r->reset( ds.get_background_rendition() ); } void posterize( void ); void ring_bell( void ) { bell_count++; } unsigned int get_bell_count( void ) const { return bell_count; } bool operator==( const Framebuffer &x ) const { return ( rows == x.rows ) && ( window_title == x.window_title ) && ( bell_count == x.bell_count ) && ( ds == x.ds ); } }; } #endif mosh-1.2.5/src/terminal/terminalfunctions.cc000066400000000000000000000424741255051512600211570ustar00rootroot00000000000000/* Mosh: the mobile shell Copyright 2012 Keith Winstein This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . In addition, as a special exception, the copyright holders give permission to link the code of portions of this program with the OpenSSL library under certain conditions as described in each individual source file, and distribute linked combinations including the two. You must obey the GNU General Public License in all respects for all of the code used other than OpenSSL. If you modify file(s) with this exception, you may extend this exception to your version of the file(s), but you are not obligated to do so. If you do not wish to do so, delete this exception statement from your version. If you delete this exception statement from all source files in the program, then also delete it here. */ #include #include #include #include "terminaldispatcher.h" #include "terminalframebuffer.h" #include "parseraction.h" using namespace Terminal; /* Terminal functions -- routines activated by CSI, escape or a control char */ static void clearline( Framebuffer *fb, int row, int start, int end ) { for ( int col = start; col <= end; col++ ) { fb->reset_cell( fb->get_mutable_cell( row, col ) ); } } /* erase in line */ static void CSI_EL( Framebuffer *fb, Dispatcher *dispatch ) { switch ( dispatch->getparam( 0, 0 ) ) { case 0: /* default: active position to end of line, inclusive */ clearline( fb, -1, fb->ds.get_cursor_col(), fb->ds.get_width() - 1 ); break; case 1: /* start of screen to active position, inclusive */ clearline( fb, -1, 0, fb->ds.get_cursor_col() ); break; case 2: /* all of line */ fb->reset_row( fb->get_mutable_row( -1 ) ); break; } } static Function func_CSI_EL( CSI, "K", CSI_EL ); /* erase in display */ static void CSI_ED( Framebuffer *fb, Dispatcher *dispatch ) { switch ( dispatch->getparam( 0, 0 ) ) { case 0: /* active position to end of screen, inclusive */ clearline( fb, -1, fb->ds.get_cursor_col(), fb->ds.get_width() - 1 ); for ( int y = fb->ds.get_cursor_row() + 1; y < fb->ds.get_height(); y++ ) { fb->reset_row( fb->get_mutable_row( y ) ); } break; case 1: /* start of screen to active position, inclusive */ for ( int y = 0; y < fb->ds.get_cursor_row(); y++ ) { fb->reset_row( fb->get_mutable_row( y ) ); } clearline( fb, -1, 0, fb->ds.get_cursor_col() ); break; case 2: /* entire screen */ for ( int y = 0; y < fb->ds.get_height(); y++ ) { fb->reset_row( fb->get_mutable_row( y ) ); } break; } } static Function func_CSI_ED( CSI, "J", CSI_ED ); /* cursor movement -- relative and absolute */ static void CSI_cursormove( Framebuffer *fb, Dispatcher *dispatch ) { int num = dispatch->getparam( 0, 1 ); switch ( dispatch->get_dispatch_chars()[ 0 ] ) { case 'A': fb->ds.move_row( -num, true ); break; case 'B': fb->ds.move_row( num, true ); break; case 'C': fb->ds.move_col( num, true ); break; case 'D': fb->ds.move_col( -num, true ); break; case 'H': case 'f': int x = dispatch->getparam( 0, 1 ); int y = dispatch->getparam( 1, 1 ); fb->ds.move_row( x - 1 ); fb->ds.move_col( y - 1 ); } } static Function func_CSI_cursormove_A( CSI, "A", CSI_cursormove ); static Function func_CSI_cursormove_B( CSI, "B", CSI_cursormove ); static Function func_CSI_cursormove_C( CSI, "C", CSI_cursormove ); static Function func_CSI_cursormove_D( CSI, "D", CSI_cursormove ); static Function func_CSI_cursormove_H( CSI, "H", CSI_cursormove ); static Function func_CSI_cursormove_f( CSI, "f", CSI_cursormove ); /* device attributes */ static void CSI_DA( Framebuffer *fb __attribute((unused)), Dispatcher *dispatch ) { dispatch->terminal_to_host.append( "\033[?62c" ); /* plain vt220 */ } static Function func_CSI_DA( CSI, "c", CSI_DA ); /* secondary device attributes */ static void CSI_SDA( Framebuffer *fb __attribute((unused)), Dispatcher *dispatch ) { dispatch->terminal_to_host.append( "\033[>1;10;0c" ); /* plain vt220 */ } static Function func_CSI_SDA( CSI, ">c", CSI_SDA ); /* screen alignment diagnostic */ static void Esc_DECALN( Framebuffer *fb, Dispatcher *dispatch __attribute((unused)) ) { for ( int y = 0; y < fb->ds.get_height(); y++ ) { for ( int x = 0; x < fb->ds.get_width(); x++ ) { fb->reset_cell( fb->get_mutable_cell( y, x ) ); fb->get_mutable_cell( y, x )->contents.push_back( L'E' ); } } } static Function func_Esc_DECALN( ESCAPE, "#8", Esc_DECALN ); /* line feed */ static void Ctrl_LF( Framebuffer *fb, Dispatcher *dispatch __attribute((unused)) ) { fb->move_rows_autoscroll( 1 ); } /* same procedure for index, vertical tab, and form feed control codes */ static Function func_Ctrl_LF( CONTROL, "\x0a", Ctrl_LF ); static Function func_Ctrl_IND( CONTROL, "\x84", Ctrl_LF ); static Function func_Ctrl_VT( CONTROL, "\x0b", Ctrl_LF ); static Function func_Ctrl_FF( CONTROL, "\x0c", Ctrl_LF ); /* carriage return */ static void Ctrl_CR( Framebuffer *fb, Dispatcher *dispatch __attribute((unused)) ) { fb->ds.move_col( 0 ); } static Function func_Ctrl_CR( CONTROL, "\x0d", Ctrl_CR ); /* backspace */ static void Ctrl_BS( Framebuffer *fb, Dispatcher *dispatch __attribute((unused)) ) { fb->ds.move_col( -1, true ); } static Function func_Ctrl_BS( CONTROL, "\x08", Ctrl_BS ); /* reverse index -- like a backwards line feed */ static void Ctrl_RI( Framebuffer *fb, Dispatcher *dispatch __attribute((unused)) ) { fb->move_rows_autoscroll( -1 ); } static Function func_Ctrl_RI( CONTROL, "\x8D", Ctrl_RI ); /* newline */ static void Ctrl_NEL( Framebuffer *fb, Dispatcher *dispatch __attribute((unused)) ) { fb->ds.move_col( 0 ); fb->move_rows_autoscroll( 1 ); } static Function func_Ctrl_NEL( CONTROL, "\x85", Ctrl_NEL ); /* horizontal tab */ static void Ctrl_HT( Framebuffer *fb, Dispatcher *dispatch __attribute((unused)) ) { int col = fb->ds.get_next_tab(); if ( col == -1 ) { /* no tabs, go to end of line */ col = fb->ds.get_width() - 1; } /* A horizontal tab is the only operation that preserves but does not set the wrap state. It also starts a new grapheme. */ bool wrap_state_save = fb->ds.next_print_will_wrap; fb->ds.move_col( col, false ); fb->ds.next_print_will_wrap = wrap_state_save; } static Function func_Ctrl_HT( CONTROL, "\x09", Ctrl_HT, false ); /* horizontal tab set */ static void Ctrl_HTS( Framebuffer *fb, Dispatcher *dispatch __attribute((unused)) ) { fb->ds.set_tab(); } static Function func_Ctrl_HTS( CONTROL, "\x88", Ctrl_HTS ); /* tabulation clear */ static void CSI_TBC( Framebuffer *fb, Dispatcher *dispatch ) { int param = dispatch->getparam( 0, 0 ); switch ( param ) { case 0: /* clear this tab stop */ fb->ds.clear_tab( fb->ds.get_cursor_col() ); break; case 3: /* clear all tab stops */ fb->ds.clear_default_tabs(); for ( int x = 0; x < fb->ds.get_width(); x++ ) { fb->ds.clear_tab( x ); } break; } } /* TBC preserves wrap state */ static Function func_CSI_TBC( CSI, "g", CSI_TBC, false ); static bool *get_DEC_mode( int param, Framebuffer *fb ) { switch ( param ) { case 1: /* cursor key mode */ return &(fb->ds.application_mode_cursor_keys); case 3: /* 80/132. Ignore but clear screen. */ /* clear screen */ fb->ds.move_row( 0 ); fb->ds.move_col( 0 ); for ( int y = 0; y < fb->ds.get_height(); y++ ) { fb->reset_row( fb->get_mutable_row( y ) ); } return NULL; case 5: /* reverse video */ return &(fb->ds.reverse_video); case 6: /* origin */ fb->ds.move_row( 0 ); fb->ds.move_col( 0 ); return &(fb->ds.origin_mode); case 7: /* auto wrap */ return &(fb->ds.auto_wrap_mode); case 25: return &(fb->ds.cursor_visible); case 1004: /* xterm mouse focus event */ return &(fb->ds.mouse_focus_event); case 1007: /* xterm mouse alternate scroll */ return &(fb->ds.mouse_alternate_scroll); case 2004: /* bracketed paste */ return &(fb->ds.bracketed_paste); } return NULL; } /* helper for CSI_DECSM and CSI_DECRM */ static void set_if_available( bool *mode, bool value ) { if ( mode ) { *mode = value; } } /* set private mode */ static void CSI_DECSM( Framebuffer *fb, Dispatcher *dispatch ) { for ( int i = 0; i < dispatch->param_count(); i++ ) { int param = dispatch->getparam( i, 0 ); if (param == 9 || (param >= 1000 && param <= 1003)) { fb->ds.mouse_reporting_mode = (Terminal::DrawState::MouseReportingMode) param; } else if (param == 1005 || param == 1006 || param == 1015) { fb->ds.mouse_encoding_mode = (Terminal::DrawState::MouseEncodingMode) param; } else { set_if_available( get_DEC_mode( param, fb ), true ); } } } /* clear private mode */ static void CSI_DECRM( Framebuffer *fb, Dispatcher *dispatch ) { for ( int i = 0; i < dispatch->param_count(); i++ ) { int param = dispatch->getparam( i, 0 ); if (param == 9 || (param >= 1000 && param <= 1003)) { fb->ds.mouse_reporting_mode = Terminal::DrawState::MOUSE_REPORTING_NONE; } else if (param == 1005 || param == 1006 || param == 1015) { fb->ds.mouse_encoding_mode = Terminal::DrawState::MOUSE_ENCODING_DEFAULT; } else { set_if_available( get_DEC_mode( param, fb ), false ); } } } /* These functions don't clear wrap state. */ static Function func_CSI_DECSM( CSI, "?h", CSI_DECSM, false ); static Function func_CSI_DECRM( CSI, "?l", CSI_DECRM, false ); static bool *get_ANSI_mode( int param, Framebuffer *fb ) { switch ( param ) { case 4: /* insert/replace mode */ return &(fb->ds.insert_mode); } return NULL; } /* set mode */ static void CSI_SM( Framebuffer *fb, Dispatcher *dispatch ) { for ( int i = 0; i < dispatch->param_count(); i++ ) { bool *mode = get_ANSI_mode( dispatch->getparam( i, 0 ), fb ); if ( mode ) { *mode = true; } } } /* clear mode */ static void CSI_RM( Framebuffer *fb, Dispatcher *dispatch ) { for ( int i = 0; i < dispatch->param_count(); i++ ) { bool *mode = get_ANSI_mode( dispatch->getparam( i, 0 ), fb ); if ( mode ) { *mode = false; } } } static Function func_CSI_SM( CSI, "h", CSI_SM ); static Function func_CSI_RM( CSI, "l", CSI_RM ); /* set top and bottom margins */ static void CSI_DECSTBM( Framebuffer *fb, Dispatcher *dispatch ) { int top = dispatch->getparam( 0, 1 ); int bottom = dispatch->getparam( 1, fb->ds.get_height() ); if ( (bottom <= top) || (top > fb->ds.get_height()) || (top == 0 && bottom == 1) ) { return; /* invalid, xterm ignores */ } fb->ds.set_scrolling_region( top - 1, bottom - 1 ); fb->ds.move_row( 0 ); fb->ds.move_col( 0 ); } static Function func_CSI_DECSTMB( CSI, "r", CSI_DECSTBM ); /* terminal bell */ static void Ctrl_BEL( Framebuffer *fb, Dispatcher *dispatch __attribute((unused)) ) { fb->ring_bell(); } static Function func_Ctrl_BEL( CONTROL, "\x07", Ctrl_BEL ); /* select graphics rendition -- e.g., bold, blinking, etc. */ static void CSI_SGR( Framebuffer *fb, Dispatcher *dispatch ) { for ( int i = 0; i < dispatch->param_count(); i++ ) { int rendition = dispatch->getparam( i, 0 ); /* We need to special-case the handling of [34]8 ; 5 ; Ps, because Ps of 0 in that case does not mean reset to default, even though it means that otherwise (as usually renditions are applied in order). */ if ((rendition == 38 || rendition == 48) && (dispatch->param_count() - i >= 3) && (dispatch->getparam( i+1, -1 ) == 5)) { (rendition == 38) ? fb->ds.set_foreground_color( dispatch->getparam( i+2, 0 ) ) : fb->ds.set_background_color( dispatch->getparam( i+2, 0 ) ); i += 2; continue; } fb->ds.add_rendition( rendition ); } } static Function func_CSI_SGR( CSI, "m", CSI_SGR, false ); /* changing renditions doesn't clear wrap flag */ /* save and restore cursor */ static void Esc_DECSC( Framebuffer *fb, Dispatcher *dispatch __attribute((unused)) ) { fb->ds.save_cursor(); } static void Esc_DECRC( Framebuffer *fb, Dispatcher *dispatch __attribute((unused)) ) { fb->ds.restore_cursor(); } static Function func_Esc_DECSC( ESCAPE, "7", Esc_DECSC ); static Function func_Esc_DECRC( ESCAPE, "8", Esc_DECRC ); /* device status report -- e.g., cursor position (used by resize) */ static void CSI_DSR( Framebuffer *fb, Dispatcher *dispatch ) { int param = dispatch->getparam( 0, 0 ); switch ( param ) { case 5: /* device status report requested */ dispatch->terminal_to_host.append( "\033[0n" ); break; case 6: /* report of active position requested */ char cpr[ 32 ]; snprintf( cpr, 32, "\033[%d;%dR", fb->ds.get_cursor_row() + 1, fb->ds.get_cursor_col() + 1 ); dispatch->terminal_to_host.append( cpr ); break; } } static Function func_CSI_DSR( CSI, "n", CSI_DSR ); /* insert line */ static void CSI_IL( Framebuffer *fb, Dispatcher *dispatch ) { int lines = dispatch->getparam( 0, 1 ); for ( int i = 0; i < lines; i++ ) { fb->insert_line( fb->ds.get_cursor_row() ); } /* vt220 manual and Ecma-48 say to move to first column */ /* but xterm and gnome-terminal don't */ fb->ds.move_col( 0 ); } static Function func_CSI_IL( CSI, "L", CSI_IL ); /* delete line */ static void CSI_DL( Framebuffer *fb, Dispatcher *dispatch ) { int lines = dispatch->getparam( 0, 1 ); for ( int i = 0; i < lines; i++ ) { fb->delete_line( fb->ds.get_cursor_row() ); } /* same story -- xterm and gnome-terminal don't move to first column */ fb->ds.move_col( 0 ); } static Function func_CSI_DL( CSI, "M", CSI_DL ); /* insert characters */ static void CSI_ICH( Framebuffer *fb, Dispatcher *dispatch ) { int cells = dispatch->getparam( 0, 1 ); for ( int i = 0; i < cells; i++ ) { fb->insert_cell( fb->ds.get_cursor_row(), fb->ds.get_cursor_col() ); } } static Function func_CSI_ICH( CSI, "@", CSI_ICH ); /* delete character */ static void CSI_DCH( Framebuffer *fb, Dispatcher *dispatch ) { int cells = dispatch->getparam( 0, 1 ); for ( int i = 0; i < cells; i++ ) { fb->delete_cell( fb->ds.get_cursor_row(), fb->ds.get_cursor_col() ); } } static Function func_CSI_DCH( CSI, "P", CSI_DCH ); /* line position absolute */ static void CSI_VPA( Framebuffer *fb, Dispatcher *dispatch ) { int row = dispatch->getparam( 0, 1 ); fb->ds.move_row( row - 1 ); } static Function func_CSI_VPA( CSI, "d", CSI_VPA ); /* character position absolute */ static void CSI_HPA( Framebuffer *fb, Dispatcher *dispatch ) { int col = dispatch->getparam( 0, 1 ); fb->ds.move_col( col - 1 ); } static Function func_CSI_CHA( CSI, "G", CSI_HPA ); /* ECMA-48 name: CHA */ static Function func_CSI_HPA( CSI, "\x60", CSI_HPA ); /* ECMA-48 name: HPA */ /* erase character */ static void CSI_ECH( Framebuffer *fb, Dispatcher *dispatch ) { int num = dispatch->getparam( 0, 1 ); int limit = fb->ds.get_cursor_col() + num - 1; if ( limit >= fb->ds.get_width() ) { limit = fb->ds.get_width() - 1; } clearline( fb, -1, fb->ds.get_cursor_col(), limit ); } static Function func_CSI_ECH( CSI, "X", CSI_ECH ); /* reset to initial state */ static void Esc_RIS( Framebuffer *fb, Dispatcher *dispatch __attribute((unused)) ) { fb->reset(); } static Function func_Esc_RIS( ESCAPE, "c", Esc_RIS ); /* soft reset */ static void CSI_DECSTR( Framebuffer *fb, Dispatcher *dispatch __attribute((unused)) ) { fb->soft_reset(); } static Function func_CSI_DECSTR( CSI, "!p", CSI_DECSTR ); /* xterm uses an Operating System Command to set the window title */ void Dispatcher::OSC_dispatch( const Parser::OSC_End *act, Framebuffer *fb ) { if ( OSC_string.size() >= 1 ) { long cmd_num = -1; int offset = 0; if ( OSC_string[ 0 ] == L';' ) { /* OSC of the form "\033];\007" */ cmd_num = 0; /* treat it as as a zero */ offset = 1; } else if ( (OSC_string.size() >= 2) && (OSC_string[ 1 ] == L';') ) { /* OSC of the form "\033]X;<title>\007" where X can be: * 0: set icon name and window title * 1: set icon name * 2: set window title */ cmd_num = OSC_string[ 0 ] - L'0'; offset = 2; } bool set_icon = (cmd_num == 0 || cmd_num == 1); bool set_title = (cmd_num == 0 || cmd_num == 2); if ( set_icon || set_title ) { fb->set_title_initialized(); std::deque<wchar_t> newtitle( OSC_string.begin() + offset, OSC_string.end() ); if ( set_icon ) { fb->set_icon_name( newtitle ); } if ( set_title ) { fb->set_window_title( newtitle ); } act->handled = true; } } } /* scroll down or terminfo indn */ static void CSI_SD( Framebuffer *fb, Dispatcher *dispatch ) { fb->scroll( dispatch->getparam( 0, 1 ) ); } static Function func_CSI_SD( CSI, "S", CSI_SD ); /* scroll up or terminfo rin */ static void CSI_SU( Framebuffer *fb, Dispatcher *dispatch ) { fb->scroll( -dispatch->getparam( 0, 1 ) ); } static Function func_CSI_SU( CSI, "T", CSI_SU ); ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������mosh-1.2.5/src/terminal/terminaluserinput.cc��������������������������������������������������������0000664�0000000�0000000�00000005444�12550515126�0021201�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* Mosh: the mobile shell Copyright 2012 Keith Winstein This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see <http://www.gnu.org/licenses/>. In addition, as a special exception, the copyright holders give permission to link the code of portions of this program with the OpenSSL library under certain conditions as described in each individual source file, and distribute linked combinations including the two. You must obey the GNU General Public License in all respects for all of the code used other than OpenSSL. If you modify file(s) with this exception, you may extend this exception to your version of the file(s), but you are not obligated to do so. If you do not wish to do so, delete this exception statement from your version. If you delete this exception statement from all source files in the program, then also delete it here. */ #include <assert.h> #include "terminaluserinput.h" using namespace Terminal; using namespace std; string UserInput::input( const Parser::UserByte *act, bool application_mode_cursor_keys ) { act->handled = true; /* The user will always be in application mode. If stm is not in application mode, convert user's cursor control function to an ANSI cursor control sequence */ /* We need to look ahead one byte in the SS3 state to see if the next byte will be A, B, C, or D (cursor control keys). */ switch ( state ) { case Ground: if ( act->c == 0x1b ) { /* ESC */ state = ESC; } return string( &act->c, 1 ); break; case ESC: if ( act->c == 'O' ) { /* ESC O = 7-bit SS3 */ state = SS3; return string(); } else { state = Ground; return string( &act->c, 1 ); } break; case SS3: state = Ground; if ( (!application_mode_cursor_keys) && (act->c >= 'A') && (act->c <= 'D') ) { char translated_cursor[ 2 ] = { '[', act->c }; return string( translated_cursor, 2 ); } else { char original_cursor[ 2 ] = { 'O', act->c }; return string( original_cursor, 2 ); } break; } /* This doesn't handle the 8-bit SS3 C1 control, which would be two octets in UTF-8. Fortunately nobody seems to send this. */ assert( false ); return string(); } ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������mosh-1.2.5/src/terminal/terminaluserinput.h���������������������������������������������������������0000664�0000000�0000000�00000003677�12550515126�0021051�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* Mosh: the mobile shell Copyright 2012 Keith Winstein This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see <http://www.gnu.org/licenses/>. In addition, as a special exception, the copyright holders give permission to link the code of portions of this program with the OpenSSL library under certain conditions as described in each individual source file, and distribute linked combinations including the two. You must obey the GNU General Public License in all respects for all of the code used other than OpenSSL. If you modify file(s) with this exception, you may extend this exception to your version of the file(s), but you are not obligated to do so. If you do not wish to do so, delete this exception statement from your version. If you delete this exception statement from all source files in the program, then also delete it here. */ #ifndef TERMINALUSERINPUT_HPP #define TERMINALUSERINPUT_HPP #include <string> #include "parseraction.h" namespace Terminal { class UserInput { public: enum UserInputState { Ground, ESC, SS3 }; private: UserInputState state; public: UserInput() : state( Ground ) {} std::string input( const Parser::UserByte *act, bool application_mode_cursor_keys ); bool operator==( const UserInput &x ) const { return state == x.state; } }; } #endif �����������������������������������������������������������������mosh-1.2.5/src/tests/�������������������������������������������������������������������������������0000775�0000000�0000000�00000000000�12550515126�0014420�5����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������mosh-1.2.5/src/tests/.gitignore���������������������������������������������������������������������0000664�0000000�0000000�00000000032�12550515126�0016403�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/ocb-aes /encrypt-decrypt ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������mosh-1.2.5/src/tests/Makefile.am��������������������������������������������������������������������0000664�0000000�0000000�00000001154�12550515126�0016455�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������AM_CXXFLAGS = $(WARNING_CXXFLAGS) $(PICKY_CXXFLAGS) $(HARDEN_CFLAGS) $(MISC_CXXFLAGS) AM_LDFLAGS = $(HARDEN_LDFLAGS) check_PROGRAMS = ocb-aes encrypt-decrypt TESTS = ocb-aes encrypt-decrypt ocb_aes_SOURCES = ocb-aes.cc test_utils.cc test_utils.h ocb_aes_CPPFLAGS = -I$(srcdir)/../crypto -I$(srcdir)/../util ocb_aes_LDADD = ../crypto/libmoshcrypto.a ../util/libmoshutil.a $(OPENSSL_LIBS) encrypt_decrypt_SOURCES = encrypt-decrypt.cc test_utils.cc test_utils.h encrypt_decrypt_CPPFLAGS = -I$(srcdir)/../crypto -I$(srcdir)/../util encrypt_decrypt_LDADD = ../crypto/libmoshcrypto.a ../util/libmoshutil.a $(OPENSSL_LIBS) ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������mosh-1.2.5/src/tests/encrypt-decrypt.cc�������������������������������������������������������������0000664�0000000�0000000�00000010375�12550515126�0020071�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* Mosh: the mobile shell Copyright 2012 Keith Winstein This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see <http://www.gnu.org/licenses/>. In addition, as a special exception, the copyright holders give permission to link the code of portions of this program with the OpenSSL library under certain conditions as described in each individual source file, and distribute linked combinations including the two. You must obey the GNU General Public License in all respects for all of the code used other than OpenSSL. If you modify file(s) with this exception, you may extend this exception to your version of the file(s), but you are not obligated to do so. If you do not wish to do so, delete this exception statement from your version. If you delete this exception statement from all source files in the program, then also delete it here. */ /* Tests the Mosh crypto layer by encrypting and decrypting a bunch of random messages, interspersed with some random bad ciphertexts which we need to reject. */ #include <stdio.h> #define __STDC_FORMAT_MACROS #include <inttypes.h> #include "crypto.h" #include "prng.h" #include "fatal_assert.h" #include "test_utils.h" using namespace Crypto; PRNG prng; const size_t MESSAGE_SIZE_MAX = (2048 - 16); const size_t MESSAGES_PER_SESSION = 256; const size_t NUM_SESSIONS = 64; bool verbose = false; #define NONCE_FMT "%016"PRIx64 static std::string random_payload( void ) { const size_t len = prng.uint32() % MESSAGE_SIZE_MAX; char *buf = new char[len]; prng.fill( buf, len ); std::string payload( buf, len ); delete [] buf; return payload; } static void test_bad_decrypt( Session &decryption_session ) { std::string bad_ct = random_payload(); bool got_exn = false; try { decryption_session.decrypt( bad_ct ); } catch ( const CryptoException &e ) { got_exn = true; /* The "bad decrypt" exception needs to be non-fatal, otherwise we are vulnerable to an easy DoS. */ fatal_assert( ! e.fatal ); } if ( verbose ) { hexdump( bad_ct, "bad ct" ); } fatal_assert( got_exn ); } /* Generate a single key and initial nonce, then perform some encryptions. */ static void test_one_session( void ) { Base64Key key; Session encryption_session( key ); Session decryption_session( key ); uint64_t nonce_int = prng.uint64(); if ( verbose ) { hexdump( key.data(), 16, "key" ); } for ( size_t i=0; i<MESSAGES_PER_SESSION; i++ ) { Nonce nonce( nonce_int ); fatal_assert( nonce.val() == nonce_int ); std::string plaintext = random_payload(); if ( verbose ) { printf( DUMP_NAME_FMT NONCE_FMT "\n", "nonce", nonce_int ); hexdump( plaintext, "pt" ); } std::string ciphertext = encryption_session.encrypt( Message( nonce, plaintext ) ); if ( verbose ) { hexdump( ciphertext, "ct" ); } Message decrypted = decryption_session.decrypt( ciphertext ); if ( verbose ) { printf( DUMP_NAME_FMT NONCE_FMT "\n", "dec nonce", decrypted.nonce.val() ); hexdump( decrypted.text, "dec pt" ); } fatal_assert( decrypted.nonce.val() == nonce_int ); fatal_assert( decrypted.text == plaintext ); nonce_int++; if ( ! ( prng.uint8() % 16 ) ) { test_bad_decrypt( decryption_session ); } if ( verbose ) { printf( "\n" ); } } } int main( int argc, char *argv[] ) { if ( argc >= 2 && strcmp( argv[ 1 ], "-v" ) == 0 ) { verbose = true; } for ( size_t i=0; i<NUM_SESSIONS; i++ ) { try { test_one_session(); } catch ( const CryptoException &e ) { fprintf( stderr, "Crypto exception: %s\r\n", e.what() ); fatal_assert( false ); } } return 0; } �������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������mosh-1.2.5/src/tests/ocb-aes.cc���������������������������������������������������������������������0000664�0000000�0000000�00000061641�12550515126�0016250�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* Mosh: the mobile shell Copyright 2012 Keith Winstein This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see <http://www.gnu.org/licenses/>. In addition, as a special exception, the copyright holders give permission to link the code of portions of this program with the OpenSSL library under certain conditions as described in each individual source file, and distribute linked combinations including the two. You must obey the GNU General Public License in all respects for all of the code used other than OpenSSL. If you modify file(s) with this exception, you may extend this exception to your version of the file(s), but you are not obligated to do so. If you do not wish to do so, delete this exception statement from your version. If you delete this exception statement from all source files in the program, then also delete it here. */ /* Test suite for the OCB-AES reference implementation included with Mosh. This tests cryptographic primitives implemented by others. It uses the same interfaces and indeed the same compiled object code as the Mosh client and server. It does not particularly test any code written for the Mosh project. */ #include <stdint.h> #include <string.h> #include <stdlib.h> #include "ae.h" #include "crypto.h" #include "prng.h" #include "fatal_assert.h" #include "test_utils.h" #define KEY_LEN 16 #define NONCE_LEN 12 #define TAG_LEN 16 using Crypto::AlignedBuffer; bool verbose = false; static bool equal( const AlignedBuffer &a, const AlignedBuffer &b ) { return ( a.len() == b.len() ) && !memcmp( a.data(), b.data(), a.len() ); } static AlignedBuffer *get_ctx( const AlignedBuffer &key ) { AlignedBuffer *ctx_buf = new AlignedBuffer( ae_ctx_sizeof() ); fatal_assert( ctx_buf ); fatal_assert( AE_SUCCESS == ae_init( (ae_ctx *)ctx_buf->data(), key.data(), key.len(), NONCE_LEN, TAG_LEN ) ); return ctx_buf; } static void scrap_ctx( AlignedBuffer *ctx_buf ) { fatal_assert( AE_SUCCESS == ae_clear( (ae_ctx *)ctx_buf->data() ) ); delete ctx_buf; } static void test_encrypt( const AlignedBuffer &key, const AlignedBuffer &nonce, const AlignedBuffer &plaintext, const AlignedBuffer &assoc, const AlignedBuffer &expected_ciphertext ) { AlignedBuffer *ctx_buf = get_ctx( key ); ae_ctx *ctx = (ae_ctx *)ctx_buf->data(); AlignedBuffer observed_ciphertext( plaintext.len() + TAG_LEN ); const int ret = ae_encrypt( ctx, nonce.data(), plaintext.data(), plaintext.len(), assoc.data(), assoc.len(), observed_ciphertext.data(), NULL, AE_FINALIZE ); if ( verbose ) { printf( "ret %d\n", ret ); hexdump(observed_ciphertext, "obs ct" ); } fatal_assert( ret == int( expected_ciphertext.len() ) ); fatal_assert( equal( expected_ciphertext, observed_ciphertext ) ); scrap_ctx( ctx_buf ); } static void test_decrypt( const AlignedBuffer &key, const AlignedBuffer &nonce, const AlignedBuffer &ciphertext, const AlignedBuffer &assoc, const AlignedBuffer &expected_plaintext, bool valid ) { AlignedBuffer *ctx_buf = get_ctx( key ); ae_ctx *ctx = (ae_ctx *)ctx_buf->data(); AlignedBuffer observed_plaintext( ciphertext.len() - TAG_LEN ); const int ret = ae_decrypt( ctx, nonce.data(), ciphertext.data(), ciphertext.len(), assoc.data(), assoc.len(), observed_plaintext.data(), NULL, AE_FINALIZE ); if ( verbose ) { printf( "ret %d\n", ret ); } if ( valid ) { if ( verbose ) { hexdump( observed_plaintext, "obs pt" ); } fatal_assert( ret == int( expected_plaintext.len() ) ); fatal_assert( equal( expected_plaintext, observed_plaintext ) ); } else { fatal_assert( ret == AE_INVALID ); } scrap_ctx( ctx_buf ); } static void test_vector( const char *key_p, const char *nonce_p, size_t assoc_len, const char *assoc_p, size_t plaintext_len, const char *plaintext_p, size_t ciphertext_len, const char *ciphertext_p ) { AlignedBuffer key ( KEY_LEN, key_p ); AlignedBuffer nonce ( NONCE_LEN, nonce_p ); AlignedBuffer plaintext ( plaintext_len, plaintext_p ); AlignedBuffer assoc ( assoc_len, assoc_p ); AlignedBuffer ciphertext( ciphertext_len, ciphertext_p ); if ( verbose ) { hexdump( key, "key" ); hexdump( nonce, "nonce" ); hexdump( assoc, "assoc" ); hexdump( plaintext, "exp pt" ); hexdump( ciphertext, "exp ct" ); } test_encrypt( key, nonce, plaintext, assoc, ciphertext ); test_decrypt( key, nonce, ciphertext, assoc, plaintext, true ); /* Try some bad ciphertexts and make sure they don't validate. */ PRNG prng; for ( size_t i=0; i<64; i++ ) { AlignedBuffer bad_ct( ciphertext.len(), ciphertext.data() ); ( (uint8_t *) bad_ct.data() )[ prng.uint32() % bad_ct.len() ] ^= ( 1 << ( prng.uint8() % 8 ) ); test_decrypt( key, nonce, bad_ct, assoc, plaintext, false ); } if (verbose) { printf( "PASSED\n\n" ); } } #define TEST_VECTOR( _key, _nonce, _assoc, _pt, _ct ) \ test_vector( _key, _nonce, sizeof(_assoc)-1, _assoc, sizeof(_pt)-1, _pt, sizeof(_ct)-1, _ct ) static void test_all_vectors( void ) { /* Test vectors from http://tools.ietf.org/html/draft-krovetz-ocb-03#appendix-A */ const char ietf_key[] = "\x00\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0A\x0B\x0C\x0D\x0E\x0F"; const char ietf_nonce[] = "\x00\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0A\x0B"; TEST_VECTOR( ietf_key, ietf_nonce , "" /* associated data */ , "" /* plaintext */ /* ciphertext including tag */ , "\x19\x7B\x9C\x3C\x44\x1D\x3C\x83\xEA\xFB\x2B\xEF\x63\x3B\x91\x82" ); TEST_VECTOR( ietf_key, ietf_nonce , "\x00\x01\x02\x03\x04\x05\x06\x07" , "\x00\x01\x02\x03\x04\x05\x06\x07" , "\x92\xB6\x57\x13\x0A\x74\xB8\x5A\x16\xDC\x76\xA4\x6D\x47\xE1\xEA" "\xD5\x37\x20\x9E\x8A\x96\xD1\x4E" ); TEST_VECTOR( ietf_key, ietf_nonce , "\x00\x01\x02\x03\x04\x05\x06\x07" , "" , "\x98\xB9\x15\x52\xC8\xC0\x09\x18\x50\x44\xE3\x0A\x6E\xB2\xFE\x21" ); TEST_VECTOR( ietf_key, ietf_nonce , "" , "\x00\x01\x02\x03\x04\x05\x06\x07" , "\x92\xB6\x57\x13\x0A\x74\xB8\x5A\x97\x1E\xFF\xCA\xE1\x9A\xD4\x71" "\x6F\x88\xE8\x7B\x87\x1F\xBE\xED" ); TEST_VECTOR( ietf_key, ietf_nonce , "\x00\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0A\x0B\x0C\x0D\x0E\x0F" , "\x00\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0A\x0B\x0C\x0D\x0E\x0F" , "\xBE\xA5\xE8\x79\x8D\xBE\x71\x10\x03\x1C\x14\x4D\xA0\xB2\x61\x22" "\x77\x6C\x99\x24\xD6\x72\x3A\x1F\xC4\x52\x45\x32\xAC\x3E\x5B\xEB" ); TEST_VECTOR( ietf_key, ietf_nonce , "\x00\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0A\x0B\x0C\x0D\x0E\x0F" , "" , "\x7D\xDB\x8E\x6C\xEA\x68\x14\x86\x62\x12\x50\x96\x19\xB1\x9C\xC6" ); TEST_VECTOR( ietf_key, ietf_nonce , "" , "\x00\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0A\x0B\x0C\x0D\x0E\x0F" , "\xBE\xA5\xE8\x79\x8D\xBE\x71\x10\x03\x1C\x14\x4D\xA0\xB2\x61\x22" "\x13\xCC\x8B\x74\x78\x07\x12\x1A\x4C\xBB\x3E\x4B\xD6\xB4\x56\xAF"); TEST_VECTOR( ietf_key, ietf_nonce , "\x00\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0A\x0B\x0C\x0D\x0E\x0F" "\x10\x11\x12\x13\x14\x15\x16\x17" , "\x00\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0A\x0B\x0C\x0D\x0E\x0F" "\x10\x11\x12\x13\x14\x15\x16\x17" , "\xBE\xA5\xE8\x79\x8D\xBE\x71\x10\x03\x1C\x14\x4D\xA0\xB2\x61\x22" "\xFC\xFC\xEE\x7A\x2A\x8D\x4D\x48\x5F\xA9\x4F\xC3\xF3\x88\x20\xF1" "\xDC\x3F\x3D\x1F\xD4\xE5\x5E\x1C" ); TEST_VECTOR( ietf_key, ietf_nonce , "\x00\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0A\x0B\x0C\x0D\x0E\x0F" "\x10\x11\x12\x13\x14\x15\x16\x17" , "" , "\x28\x20\x26\xDA\x30\x68\xBC\x9F\xA1\x18\x68\x1D\x55\x9F\x10\xF6" ); TEST_VECTOR( ietf_key, ietf_nonce , "" , "\x00\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0A\x0B\x0C\x0D\x0E\x0F" "\x10\x11\x12\x13\x14\x15\x16\x17" , "\xBE\xA5\xE8\x79\x8D\xBE\x71\x10\x03\x1C\x14\x4D\xA0\xB2\x61\x22" "\xFC\xFC\xEE\x7A\x2A\x8D\x4D\x48\x6E\xF2\xF5\x25\x87\xFD\xA0\xED" "\x97\xDC\x7E\xED\xE2\x41\xDF\x68" ); TEST_VECTOR( ietf_key, ietf_nonce , "\x00\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0A\x0B\x0C\x0D\x0E\x0F" "\x10\x11\x12\x13\x14\x15\x16\x17" , "\x00\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0A\x0B\x0C\x0D\x0E\x0F" "\x10\x11\x12\x13\x14\x15\x16\x17" , "\xBE\xA5\xE8\x79\x8D\xBE\x71\x10\x03\x1C\x14\x4D\xA0\xB2\x61\x22" "\xFC\xFC\xEE\x7A\x2A\x8D\x4D\x48\x5F\xA9\x4F\xC3\xF3\x88\x20\xF1" "\xDC\x3F\x3D\x1F\xD4\xE5\x5E\x1C" ); TEST_VECTOR( ietf_key, ietf_nonce , "\x00\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0A\x0B\x0C\x0D\x0E\x0F\x10\x11\x12\x13\x14\x15\x16\x17" , "" , "\x28\x20\x26\xDA\x30\x68\xBC\x9F\xA1\x18\x68\x1D\x55\x9F\x10\xF6"); TEST_VECTOR( ietf_key, ietf_nonce , "" , "\x00\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0A\x0B\x0C\x0D\x0E\x0F" "\x10\x11\x12\x13\x14\x15\x16\x17" , "\xBE\xA5\xE8\x79\x8D\xBE\x71\x10\x03\x1C\x14\x4D\xA0\xB2\x61\x22" "\xFC\xFC\xEE\x7A\x2A\x8D\x4D\x48\x6E\xF2\xF5\x25\x87\xFD\xA0\xED" "\x97\xDC\x7E\xED\xE2\x41\xDF\x68" ); TEST_VECTOR( ietf_key, ietf_nonce , "\x00\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0A\x0B\x0C\x0D\x0E\x0F" "\x10\x11\x12\x13\x14\x15\x16\x17\x18\x19\x1A\x1B\x1C\x1D\x1E\x1F" , "\x00\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0A\x0B\x0C\x0D\x0E\x0F" "\x10\x11\x12\x13\x14\x15\x16\x17\x18\x19\x1A\x1B\x1C\x1D\x1E\x1F" , "\xBE\xA5\xE8\x79\x8D\xBE\x71\x10\x03\x1C\x14\x4D\xA0\xB2\x61\x22" "\xCE\xAA\xB9\xB0\x5D\xF7\x71\xA6\x57\x14\x9D\x53\x77\x34\x63\xCB" "\xB2\xA0\x40\xDD\x3B\xD5\x16\x43\x72\xD7\x6D\x7B\xB6\x82\x42\x40" ); TEST_VECTOR( ietf_key, ietf_nonce , "\x00\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0A\x0B\x0C\x0D\x0E\x0F" "\x10\x11\x12\x13\x14\x15\x16\x17\x18\x19\x1A\x1B\x1C\x1D\x1E\x1F" , "" , "\xE1\xE0\x72\x63\x3B\xAD\xE5\x1A\x60\xE8\x59\x51\xD9\xC4\x2A\x1B" ); TEST_VECTOR( ietf_key, ietf_nonce , "" , "\x00\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0A\x0B\x0C\x0D\x0E\x0F" "\x10\x11\x12\x13\x14\x15\x16\x17\x18\x19\x1A\x1B\x1C\x1D\x1E\x1F" , "\xBE\xA5\xE8\x79\x8D\xBE\x71\x10\x03\x1C\x14\x4D\xA0\xB2\x61\x22" "\xCE\xAA\xB9\xB0\x5D\xF7\x71\xA6\x57\x14\x9D\x53\x77\x34\x63\xCB" "\x4A\x3B\xAE\x82\x44\x65\xCF\xDA\xF8\xC4\x1F\xC5\x0C\x7D\xF9\xD9" ); TEST_VECTOR( ietf_key, ietf_nonce , "\x00\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0A\x0B\x0C\x0D\x0E\x0F" "\x10\x11\x12\x13\x14\x15\x16\x17\x18\x19\x1A\x1B\x1C\x1D\x1E\x1F" "\x20\x21\x22\x23\x24\x25\x26\x27" , "\x00\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0A\x0B\x0C\x0D\x0E\x0F" "\x10\x11\x12\x13\x14\x15\x16\x17\x18\x19\x1A\x1B\x1C\x1D\x1E\x1F" "\x20\x21\x22\x23\x24\x25\x26\x27" , "\xBE\xA5\xE8\x79\x8D\xBE\x71\x10\x03\x1C\x14\x4D\xA0\xB2\x61\x22" "\xCE\xAA\xB9\xB0\x5D\xF7\x71\xA6\x57\x14\x9D\x53\x77\x34\x63\xCB" "\x68\xC6\x57\x78\xB0\x58\xA6\x35\x65\x9C\x62\x32\x11\xDE\xEA\x0D" "\xE3\x0D\x2C\x38\x18\x79\xF4\xC8" ); TEST_VECTOR( ietf_key, ietf_nonce , "\x00\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0A\x0B\x0C\x0D\x0E\x0F" "\x10\x11\x12\x13\x14\x15\x16\x17\x18\x19\x1A\x1B\x1C\x1D\x1E\x1F" "\x20\x21\x22\x23\x24\x25\x26\x27" , "" , "\x7A\xEB\x7A\x69\xA1\x68\x7D\xD0\x82\xCA\x27\xB0\xD9\xA3\x70\x96" ); TEST_VECTOR( ietf_key, ietf_nonce , "" , "\x00\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0A\x0B\x0C\x0D\x0E\x0F" "\x10\x11\x12\x13\x14\x15\x16\x17\x18\x19\x1A\x1B\x1C\x1D\x1E\x1F" "\x20\x21\x22\x23\x24\x25\x26\x27" , "\xBE\xA5\xE8\x79\x8D\xBE\x71\x10\x03\x1C\x14\x4D\xA0\xB2\x61\x22" "\xCE\xAA\xB9\xB0\x5D\xF7\x71\xA6\x57\x14\x9D\x53\x77\x34\x63\xCB" "\x68\xC6\x57\x78\xB0\x58\xA6\x35\x06\x0C\x84\x67\xF4\xAB\xAB\x5E" "\x8B\x3C\x20\x67\xA2\xE1\x15\xDC" ); /* Some big texts. These were originally encrypted using this program; they are regression tests. */ TEST_VECTOR( "\x06\xF8\x9F\x69\xDA\x49\xDA\xD7\x68\x48\xFF\xB3\x60\xB6\x8F\x00" , "\xDC\xBF\x85\x18\x23\xD9\x67\x85\x45\x59\x6F\xAD" , "" , "\xC4\x8E\x1F\x04\x10\x2F\xA5\x58\x68\x42\x62\xF3\x1B\xE7\x63\xA7" "\x77\x89\x64\x16\xE6\xB0\xF7\xFA\xFE\xF0\xB9\x50\x22\xDC\xCE\x78" "\xA5\x01\xA4\x2D\xA2\x0F\x50\xEA\x9A\xAE\x23\x60\x1C\xC9\x11\x84" "\x5F\xD0\x0A\x88\x99\xCD\xF1\x1B\x7C\xF9\x71\xC2\xD8\xE3\x7B\xB1" "\xBC\x91\xCE\x10\xDA\xED\xBF\xA5\xDF\x96\x69\x7A\xFA\xCA\x9A\x91" "\x40\xF9\x8F\xF1\xFE\x5A\x2B\x21\x17\xA5\xAE\x97\xBD\x0B\x68\xE0" "\x58\x7A\xB4\x10\x89\xDE\x30\x12\xB7\x14\xEB\xE8\xFA\xA5\x87\x7A" "\x7E\xEA\xD0\x34\x0B\xAF\x73\x0B\x1C\xA7\xC5\xCB\x84\x15\x5F\x45" "\x53\x8E\x27\xD7\xA0\x7D\xAF\x99\x05\x6E\x9F\xF4\xCD\xE5\xA1\x11" "\x43\xBD\x7A\x24\xA3\xB7\xB6\x92\x7C\xB7\xD8\x6F\x17\x78\xB3\x30" "\x53\x6E\x46\x14\x11\x86\xF6\x70\x2E\x25\x8E\x54\xF8\x70\x2F\x47" "\x2B\xF8\xF6\xC3\xEE\xFB\x9B\x7D\xF0\x73\xAD\x24\xC0\xE9\x58\xA8" "\x79\xDC\x92\x25\x6D\xF3\x9D\xA8\x8F\x07\xA3\xE7\xAB\xF4\xBC\x32" "\x29\xB6\xEF\xDB\x20\x42\x51\xCF\xD5\x57\xDA\x14\xC7\x2A\xB3\x19" "\xDE\x31\xF7\x77\x95\x8D\x33\x57\x1D\xEB\x9A\x80\x98\x4D\x95\x07" "\xEB\x38\x42\xEB\xE0\xAB\x2D\xF3\x9E\x16\xFA\x6D\x06\x62\x41\x58" "\x81\xBF\x3D\x01\x1A\xF7\xBA\x53\xCE\x60\xEF\x86\xC9\x11\xED\x89" "\x5B\xCF\x98\xB4\xD3\xD4\xAC\xB9\x71\x69\xEE\xF6\xA7\x77\x4E\x46" "\x0F\xF7\x1D\x7D\x36\x4E\x86\x15\xCD\xF3\xD3\x5C\xAA\x97\x74\xCC" "\x5D\xD6\xF0\x5C\x62\x27\x0D\x4B\x01\xDD\x8B\xE6\x2C\x72\x99\xBA" "\x5B\x95\x87\x0F\xB9\x9A\xBE\xA2\x42\x5E\x7D\x1B\x19\x08\xBC\xEF" "\x66\x24\x2F\xB3\xFC\xE7\xDA\xB1\x79\x51\xE9\x3B\x45\x66\xDC\xD8" "\x0F\xA7\x56\xBF\x56\x15\x4B\x15\xCC\x3E\x9F\x44\xB9\xED\xC2\x0A" "\x7C\xA9\x6D\x87\xCD\xE2\x00\x9F\x2F\x2C\xF0\xBC\x1B\xB0\x27\xE8" "\x46\x3E\x06\x1E\xD5\xB6\xB4\x82\xF7\x88\xB7\x48\xF3\x58\xB0\x23" "\x6D\xC2\xD3\x85\x9D\x1F\xE5\x30\xFC\xC8\x46\x62\xD1\xE1\xAE\x3B" "\x3A\xA9\x57\xE0\xD7\x8F\x7E\x4D\x59\x7D\x7A\xEB\xBF\xD3\x10\x00" "\xA0\xE4\x76\x76\xE3\xA8\x14\xD0\x03\xBF\x1F\x6A\xF9\x11\xCE\x98" "\x2C\x2A\x86\x25\x77\x85\x14\x76\xD4\x51\xAB\xC7\x3A\xA7\xE1\xF7" "\x23\xF7\x2B\xA3\xBA\xE4\x0B\xA4\x81\x9A\x83\x98\x69\xC3\x1C\x8A" "\xBD\x26\x12\x36\x22\x9D\xCE\x85\x5D\xA3\xA0\xDF\x66\xD0\x59\xF6" "\x47\xF2\xC5\x37\xF1\x62\x0D\x0C\x45\x5B\xE5\xFE\x3C\x8D\x28\x75" , "\xa1\xd8\xa0\xe0\x75\x5c\xb4\xf4\xab\x59\x6d\x14\xfc\x2e\x75\x54" "\xa3\x35\x4f\x57\x69\x48\x7a\x46\x17\x5f\xd9\x34\x50\xf9\x35\xe5" "\x6f\xee\x27\xdb\x28\x0f\x06\x0b\xaf\xd5\x50\x4e\x20\x78\x35\xd6" "\x4d\xa0\x18\xe8\x6c\x5b\x07\xbb\xb6\xd0\x3f\x4a\x0e\x14\x32\xaf" "\x0d\x6a\x5d\xb6\xe4\x36\xb0\x1c\xae\x2e\x75\x85\x19\x53\x9d\xf2" "\xc6\xc6\xf5\x29\x4a\x5d\x73\xd6\xcd\x3a\xec\x38\x15\x7b\x1f\x3d" "\x19\x8f\x7f\x85\x53\x75\xfe\x6c\x8a\x5d\x4f\x05\x3f\x16\x5e\x73" "\x9c\xbe\xbe\xdb\xe8\xb2\x3e\xea\x0f\x2e\x2c\xf3\x2c\x9c\x56\x5b" "\xf7\xf2\xed\x87\x07\xfb\x87\x74\x09\xda\xb8\xad\x57\xc0\xa0\x79" "\xc6\x69\x1c\x79\x08\x69\x35\x18\xc4\x54\xca\x6b\xed\x89\xa0\x27" "\x32\x19\x1f\xaf\xff\x12\x1c\x1b\x08\xa6\x9f\x0e\xe3\x01\x98\x77" "\xdb\x75\xdf\x87\x67\x6d\xd9\xb4\x23\x39\xd0\x81\x54\xf8\x89\x18" "\x5c\xbd\xb7\x4d\xf4\xb1\x8b\x37\x2e\x9b\x20\x29\x9f\x95\x9e\xdd" "\x73\x2c\xdb\x37\xe5\x7d\x9f\x8a\x79\xff\x8f\x5b\x99\x10\xe1\xe9" "\x1d\xcb\x1f\x17\xef\x7d\xb7\xd1\x50\x23\x4a\x38\x92\xfb\xbe\x1c" "\xff\x23\x68\x3a\xb9\xc8\xbc\xa0\xab\xa7\xbe\xc8\x84\xce\x66\xa7" "\x11\x17\xc6\x48\x91\x4d\x77\xe1\x64\xc4\x26\x08\xcd\xb4\xea\x10" "\x42\x60\x5a\x7c\x5a\x72\xba\xb4\x93\xf7\x02\xa1\xd4\x44\xdb\x1a" "\x4a\xc3\xb1\xea\x69\x74\xea\x18\xb3\x5a\x27\x09\x6f\x5b\x1f\x30" "\xe2\xeb\x2a\x37\x5e\xd2\xe2\x23\xec\xbc\xcb\xcb\x65\x41\xed\x0e" "\x23\xda\x71\x45\xf3\x3f\x7d\x44\x73\xd7\x14\x4d\xab\x03\xf7\x7d" "\x24\x33\x9d\x25\x83\x1f\x3d\xf3\xc9\x34\x42\x6c\x2f\x61\xa2\x83" "\xa0\x0f\xb6\x28\xe8\x27\xf0\x91\xf6\xc5\x7e\xa1\x74\x74\xaf\xa7" "\x7f\xba\x99\x29\xb2\x91\x9f\xf0\xf7\x71\x1f\xf0\x0b\xd6\xd9\xf9" "\x72\xaa\x04\x09\x5d\x75\x76\x5f\x18\xc8\xd5\xd5\x84\x1a\x40\x3f" "\xe5\x38\xe4\x38\xdf\xf9\x0d\x79\xdc\x71\xc8\xa8\xef\x86\x4e\x26" "\xf4\xc2\x46\xf4\xe0\x64\xd6\x5e\xfb\x1c\x47\x58\x3d\x87\x7c\xba" "\xa3\xf5\x98\xdc\xd5\xdf\xaf\x62\x96\xee\x4e\x39\x32\x4c\x6d\xd2" "\x0a\x6f\xf8\x34\x98\x76\xae\x21\xa1\x41\x3d\x96\xfc\x52\xdb\xdd" "\xf3\x9a\xb1\xf3\x78\x3d\xb8\x2f\xae\xae\x7d\xe0\x4b\xb2\xdf\x2b" "\x12\xac\xee\xfb\xf8\x54\x60\xea\x74\xd5\xde\x43\x7b\x38\x45\x5f" "\xa0\xb8\xe8\xcc\x8a\xe3\xcc\x0c\x92\xe6\xb1\xb0\xe2\xc1\x99\xca" "\x1b\xa1\xac\x6e\x7a\x8a\xa0\x20\x3d\xeb\x29\x8b\xf4\x55\x41\x62" ); TEST_VECTOR( "\x7A\x54\x0D\x3E\x56\x38\xF7\xC6\xCF\xAB\xF9\x56\xDC\xCA\x14\x23" , "\x9B\x0E\xC1\x15\xD5\xE6\xC9\xAB\xE6\x88\x2A\x18" , "" , "\x52\xDB\xA7\x44\x2B\x1C\x9C\x24\x4D\xF3\xA1\xE4\x53\x7B\x9B\xB2" "\x25\xC5\xA3\x81\x42\x23\xA9\xB4\x12\xF8\xFC\xE4\xF6\x8E\x20\xD4" "\x59\x7B\x39\x2D\x5D\x7C\x6E\xB7\x51\x02\x90\x7A\x8E\xAA\x30\xD0" "\xEB\xDF\x70\x09\x5A\xEC\xFB\xD4\xDB\x0B\xE9\x1B\x79\xAF\x40\xA3" "\xC9\xD1\x3F\x0E\x7E\x9B\xA8\xF2\x4E\xA2\xAE\x81\xE7\x8F\xE9\x4A" "\xF8\x84\x38\x20\x9B\xCB\x0D\x9E\x46\x87\xCC\x0C\x71\xF2\x4C\xE5" "\x57\x47\xFB\x52\x1D\x0C\x92\xCD\x9B\xD0\xA3\xA3\xFE\x44\x4F\x2A" "\x2D\x7D\xC8\x48\x87\x71\x8A\x83\x83\x43\x52\xD9\x87\x72\x0A\x1F" "\x72\x7D\xE0\x0D\xE4\xBA\x38\x2D\xAB\x89\x85\x34\xFE\x30\xE9\x47" "\x99\x69\x96\x37\x3B\x88\x0D\x08\x5B\x41\xF0\x55\x3A\x90\xB6\x5D" "\xC9\xB8\xFA\x22\xEB\x54\x67\x1B\x43\x3B\x2A\x13\xDF\x76\x71\xBF" "\x8F\xBA\xF6\x5F\xE0\xAE\x84\x80\xD3\x9A\x9E\xFE\x35\x29\x0C\x44" "\xB8\x3B\xC8\x58\x3F\x51\x18\xF0\x8E\x3F\xE0\xFE\xFF\x55\xE1\xFD" "\x79\xED\x71\x6D\xB9\xBE\xF0\x76\x25\x4E\x6F\xA2\x49\x5B\xEC\x20" "\x61\x95\x65\x67\xBF\xA3\x73\x18\x65\x7C\x2F\x94\x29\x3B\x57\x7D" "\x78\x6A\xBD\x22\x2B\x17\x5E\xDA\x2B\x4E\xC3\xA5\xF0\x9A\xD9\x1C" "\xCF\xC4\xDD\x7E\x07\xB4\xE6\x50\xD0\x9D\xAF\x86\x9D\x3F\xFE\x70" "\x64\x18\xBA\x32\x90\xE5\x63\xA0\x08\x3D\x1D\xCE\xB2\x77\x31\x59" "\xD4\xB4\x03\x2C\xE2\x15\x11\x17\xD1\xBA\x5E\x1E\x7D\x38\x09\xCF" "\x25\x3E\x3D\x89\x7E\x13\xAE\x11\xFA\xCD\x46\x23\x86\xC5\x78\xC8" "\x1A\xE8\x8F\x9A\x9C\xCE\xBE\x01\x5D\x70\x56\xA8\x84\x07\x07\x2A" "\x1C\xD4\x33\x8B\x2F\x01\x0F\xD0\xCC\xCD\x29\xEC\x73\x6E\xDE\xB2" "\xBC\xA7\x32\x6A\x12\x93\x13\xCD\xE0\xA9\x55\x97\x36\x6B\xDA\xC5" "\xC5\xD2\x44\x80\xBD\x00\x37\xD7\x21\x80\x27\x1F\x2E\x02\x76\x1A" "\x57\xA5\x5F\xCE\x8B\x20\x6A\x6A\x0F\x70\x9B\xDB\x07\x64\x96\x7B" "\x43\xFD\xCA\xBF\x9D\x52\xEB\x24\x7B\x1B\xEE\x43\x10\x2C\xDB\x92" "\xA1\xA8\xA9\xF7\x2F\xD2\x39\xA8\x85\x9C\xFE\x2C\x2A\xCF\x52\x73" "\xFB\xCA\x20\xAD\xC9\xDD\xFC\x4A\x91\x39\x6C\x7C\x84\x67\xC5\xE4" "\x9B\x3E\x3D\x6B\x56\x3B\x2B\xDC\x8A\x46\xF6\x7C\x36\xF9\x27\x29" "\x37\x38\x7C\x9D\xA0\x6E\x5D\x4C\xE5\xB2\x6F\x0C\xDC\xEF\xFE\x35" "\xFE\x3D\x56\x40\x7F\xBD\x4D\xDD\x40\x79\xDD\xA7\x0A\x7B\xA2\xCE" "\x22\x38\x94\xEA\x90\xF5\x95\xB6\xE6\x6F\x14\xFB\xA2" , "\xec\xa9\xcc\x30\x66\x6c\x04\x16\x21\x8d\xc8\x15\x47\xa2\x18\xcf" "\x19\x90\x4f\x82\x27\x25\xa2\x1e\xfa\x1c\xe4\x58\x78\x43\x52\x4c" "\xac\x24\xde\xcb\xad\x80\x05\x7a\xeb\x2d\xc0\x33\x05\x31\x25\x44" "\xd7\x11\xa1\xf2\xcb\x09\x6f\xf0\x14\x3c\x3f\xf2\xc7\x79\xfb\x3f" "\xb0\x0a\x65\xae\xd7\xe5\x5d\x35\x0c\xb4\x69\x7a\x89\x6b\xa9\xdc" "\x02\x69\x96\xd2\x9f\xe7\x3c\x99\xd4\xd3\x55\x97\xc4\x59\xad\xc4" "\x0c\x7b\xf8\x47\x1c\xbe\x36\x2a\x53\x6b\xb1\x21\x5f\xc1\x6e\xca" "\x0a\x4f\x16\x3a\xf0\xd4\x12\xa6\xf8\x68\x9f\x12\xad\x36\x4c\xd8" "\x5a\x5b\x17\xb8\xbd\xc7\x2c\x48\x51\x7d\x45\x74\x00\xb0\x02\xe9" "\x1b\xd6\x0c\x41\xa7\x5d\x65\xca\x68\xa7\x3e\x3c\xf5\xaa\x9b\xbd" "\x25\x98\x00\xd8\x4d\xbc\xd1\x7a\x25\x34\x92\x24\xa4\x84\x62\x63" "\x2c\x40\xa5\x58\x81\x90\xf1\x0f\x75\xaa\x70\xe4\x4e\x0f\xa3\x03" "\x90\xd1\x07\x18\x0a\x50\x9a\x3e\x28\x1f\x33\xb7\x11\xed\x3c\x2c" "\x40\xc8\xd7\xe3\x12\xdc\xef\x94\x93\x7b\x11\xc3\x24\x51\x61\xbf" "\x8b\xa4\xa4\x5c\x85\x0d\x50\x49\x45\x69\xbe\x5b\x36\x90\x84\x30" "\x66\x67\x76\x3d\xcc\x02\x8b\x9f\xb7\x90\x57\xef\xe1\x21\x34\x65" "\x3e\xca\xbf\x70\x1d\x76\x63\xbf\xae\x1f\xb2\x55\xf0\x87\x3e\x42" "\xf9\x71\x28\x02\x06\x9e\xf7\x6a\x47\x3b\xda\x38\x54\x66\xd9\xaf" "\xba\x7b\xec\xbf\xe3\x52\x63\x02\x8b\xa7\xad\x1d\x76\x16\xa2\x20" "\x38\xec\x40\xb7\xc8\x35\x6b\xc2\x80\x9d\x20\x02\xc6\x34\xdb\x65" "\xd8\x27\x0b\xc5\x2d\x85\xe4\xdc\x85\xae\x10\x36\x01\xdb\x4b\xaf" "\x44\x79\xea\x23\x21\xa0\x83\xa3\x91\xf5\xc5\x16\x9b\xeb\x43\x92" "\x1f\x88\xd2\x00\x60\x40\xe9\x52\x0b\x39\x86\x3b\x9e\x3b\x9a\x4a" "\x31\xdf\xb6\x57\x78\x38\xcf\x77\x7c\x0c\xf4\x14\x90\x25\xed\x27" "\xd2\x86\x20\x4c\x1a\x52\xeb\xbe\x1e\xac\x2b\xce\xb7\x72\x86\x87" "\xfd\xac\x11\x90\xc5\xea\x96\xcb\xdc\x89\xe9\x77\xf0\x83\xc3\xa7" "\xa7\xd1\xe1\xc9\x7e\x89\xb3\x4e\xf1\x12\xa3\x9c\xfe\x66\xcc\x5d" "\xcf\x1d\x5a\x11\x21\x2f\x10\x66\x37\x5f\xd7\x35\xeb\x09\x62\x99" "\xa6\xf8\xc7\xc7\xef\xd3\xf0\x56\x2b\xa7\x14\x65\x6a\xce\xa9\x68" "\xe7\xa4\x89\xb4\x1e\x16\x99\xbf\x8d\x2d\x5e\x67\xb4\x3a\x0b\xf3" "\x37\x14\x1e\x5d\xc6\xb4\xb5\x9e\xa5\x69\xa4\xaf\xcc\x0f\x46\xe9" "\xd5\xbb\x10\x49\x07\x0d\x92\x42\x0c\x04\xb9\xdf\xa4\xb5\xef\xcc" "\x05\x81\x3f\xc1\x21\x12\x2c\x33\xb7\x79\xfd\x5d\x8a" ); } /* http://tools.ietf.org/html/draft-krovetz-ocb-03#appendix-A also specifies an iterative test algorithm, which we implement here. */ static void test_iterative( void ) { /* Key is always all zeros */ AlignedBuffer key( KEY_LEN ); memset( key.data(), 0, KEY_LEN ); AlignedBuffer *ctx_buf = get_ctx( key ); ae_ctx *ctx = (ae_ctx *)ctx_buf->data(); AlignedBuffer nonce( NONCE_LEN ); memset( nonce.data(), 0, NONCE_LEN ); /* The loop below fills this buffer in order. Each iteration adds 2*i + 3*TAG_LEN bytes. */ AlignedBuffer accumulator( 22400 ); uint8_t *acc = (uint8_t *) accumulator.data(); for ( size_t i=0; i<128; i++ ) { /* i bytes of zeros */ AlignedBuffer s( i ); memset( s.data(), 0, s.len() ); /* Nonce is 11 bytes of zeros followed by 1 byte i */ ( (uint8_t *) nonce.data() )[ 11 ] = i; /* We can't write directly to acc because it might not be aligned. */ AlignedBuffer out( s.len() + TAG_LEN ); /* OCB-ENCRYPT(K,N,S,S) */ fatal_assert( 0 <= ae_encrypt( ctx, nonce.data(), s.data(), s.len(), s.data(), s.len(), out.data(), NULL, AE_FINALIZE ) ); memcpy( acc, out.data(), s.len() + TAG_LEN ); acc += s.len() + TAG_LEN; /* OCB-ENCRYPT(K,N,<empty string>,S) */ fatal_assert( 0 <= ae_encrypt( ctx, nonce.data(), s.data(), s.len(), NULL, 0, out.data(), NULL, AE_FINALIZE ) ); memcpy( acc, out.data(), s.len() + TAG_LEN ); acc += s.len() + TAG_LEN; /* OCB-ENCRYPT(K,N,S,<empty string>) */ fatal_assert( 0 <= ae_encrypt( ctx, nonce.data(), NULL, 0, s.data(), s.len(), out.data(), NULL, AE_FINALIZE ) ); memcpy( acc, out.data(), TAG_LEN ); acc += TAG_LEN; } /* OCB-ENCRYPT(K,N,C,<empty string>) */ AlignedBuffer out( TAG_LEN ); memset( nonce.data(), 0, NONCE_LEN ); fatal_assert( 0 <= ae_encrypt( ctx, nonce.data(), NULL, 0, accumulator.data(), accumulator.len(), out.data(), NULL, AE_FINALIZE ) ); /* Check this final tag against the known value */ AlignedBuffer correct( TAG_LEN, "\xB2\xB4\x1C\xBF\x9B\x05\x03\x7D\xA7\xF1\x6C\x24\xA3\x5C\x1C\x94" ); fatal_assert( equal( out, correct ) ); scrap_ctx( ctx_buf ); if ( verbose ) { printf( "iterative PASSED\n\n" ); } } int main( int argc, char *argv[] ) { if ( argc >= 2 && strcmp( argv[ 1 ], "-v" ) == 0 ) { verbose = true; } test_all_vectors(); test_iterative(); return 0; } �����������������������������������������������������������������������������������������������mosh-1.2.5/src/tests/test_utils.cc������������������������������������������������������������������0000664�0000000�0000000�00000003702�12550515126�0017130�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* Mosh: the mobile shell Copyright 2012 Keith Winstein This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see <http://www.gnu.org/licenses/>. In addition, as a special exception, the copyright holders give permission to link the code of portions of this program with the OpenSSL library under certain conditions as described in each individual source file, and distribute linked combinations including the two. You must obey the GNU General Public License in all respects for all of the code used other than OpenSSL. If you modify file(s) with this exception, you may extend this exception to your version of the file(s), but you are not obligated to do so. If you do not wish to do so, delete this exception statement from your version. If you delete this exception statement from all source files in the program, then also delete it here. */ #include <stdio.h> #include "test_utils.h" void hexdump( const void *buf, size_t len, const char *name ) { const unsigned char *data = (const unsigned char *) buf; printf( DUMP_NAME_FMT, name ); for ( size_t i = 0; i < len; i++ ) { printf( "%02x", data[ i ] ); } printf( "\n" ); } void hexdump( const Crypto::AlignedBuffer &buf, const char *name ) { hexdump( buf.data(), buf.len(), name ); } void hexdump( const std::string &buf, const char *name ) { hexdump( buf.data(), buf.size(), name ); } ��������������������������������������������������������������mosh-1.2.5/src/tests/test_utils.h�������������������������������������������������������������������0000664�0000000�0000000�00000003372�12550515126�0016775�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* Mosh: the mobile shell Copyright 2012 Keith Winstein This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see <http://www.gnu.org/licenses/>. In addition, as a special exception, the copyright holders give permission to link the code of portions of this program with the OpenSSL library under certain conditions as described in each individual source file, and distribute linked combinations including the two. You must obey the GNU General Public License in all respects for all of the code used other than OpenSSL. If you modify file(s) with this exception, you may extend this exception to your version of the file(s), but you are not obligated to do so. If you do not wish to do so, delete this exception statement from your version. If you delete this exception statement from all source files in the program, then also delete it here. */ #ifndef TEST_UTILS_HPP #define TEST_UTILS_HPP #include <string> #include "crypto.h" #define DUMP_NAME_FMT "%-10s " void hexdump( const void *buf, size_t len, const char *name ); void hexdump( const Crypto::AlignedBuffer &buf, const char *name ); void hexdump( const std::string &buf, const char *name ); #endif ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������mosh-1.2.5/src/util/��������������������������������������������������������������������������������0000775�0000000�0000000�00000000000�12550515126�0014233�5����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������mosh-1.2.5/src/util/Makefile.am���������������������������������������������������������������������0000664�0000000�0000000�00000000446�12550515126�0016273�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������AM_CXXFLAGS = $(WARNING_CXXFLAGS) $(PICKY_CXXFLAGS) $(HARDEN_CFLAGS) $(MISC_CXXFLAGS) noinst_LIBRARIES = libmoshutil.a libmoshutil_a_SOURCES = locale_utils.cc locale_utils.h swrite.cc swrite.h dos_assert.h fatal_assert.h select.h select.cc timestamp.h timestamp.cc pty_compat.cc pty_compat.h ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������mosh-1.2.5/src/util/dos_assert.h��������������������������������������������������������������������0000664�0000000�0000000�00000004012�12550515126�0016547�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* Mosh: the mobile shell Copyright 2012 Keith Winstein This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see <http://www.gnu.org/licenses/>. In addition, as a special exception, the copyright holders give permission to link the code of portions of this program with the OpenSSL library under certain conditions as described in each individual source file, and distribute linked combinations including the two. You must obey the GNU General Public License in all respects for all of the code used other than OpenSSL. If you modify file(s) with this exception, you may extend this exception to your version of the file(s), but you are not obligated to do so. If you do not wish to do so, delete this exception statement from your version. If you delete this exception statement from all source files in the program, then also delete it here. */ #ifndef DOS_ASSERT_HPP #define DOS_ASSERT_HPP #include <stdio.h> #include <stdlib.h> #include "crypto.h" static void dos_detected( const char *expression, const char *file, int line, const char *function ) { char buffer[ 2048 ]; snprintf( buffer, 2048, "Illegal counterparty input (possible denial of service) in function %s at %s:%d, failed test: %s\n", function, file, line, expression ); throw Crypto::CryptoException( buffer ); } #define dos_assert(expr) \ ((expr) \ ? (void)0 \ : dos_detected (#expr, __FILE__, __LINE__, __func__ )) #endif ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������mosh-1.2.5/src/util/fatal_assert.h������������������������������������������������������������������0000664�0000000�0000000�00000003641�12550515126�0017060�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* Mosh: the mobile shell Copyright 2012 Keith Winstein This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see <http://www.gnu.org/licenses/>. In addition, as a special exception, the copyright holders give permission to link the code of portions of this program with the OpenSSL library under certain conditions as described in each individual source file, and distribute linked combinations including the two. You must obey the GNU General Public License in all respects for all of the code used other than OpenSSL. If you modify file(s) with this exception, you may extend this exception to your version of the file(s), but you are not obligated to do so. If you do not wish to do so, delete this exception statement from your version. If you delete this exception statement from all source files in the program, then also delete it here. */ #ifndef FATAL_ASSERT_HPP #define FATAL_ASSERT_HPP #include <stdio.h> #include <stdlib.h> static void fatal_error( const char *expression, const char *file, int line, const char *function ) { fprintf( stderr, "Fatal assertion failure in function %s at %s:%d\nFailed test: %s\n", function, file, line, expression ); abort(); } #define fatal_assert(expr) \ ((expr) \ ? (void)0 \ : fatal_error (#expr, __FILE__, __LINE__, __func__ )) #endif �����������������������������������������������������������������������������������������������mosh-1.2.5/src/util/locale_utils.cc�����������������������������������������������������������������0000664�0000000�0000000�00000007243�12550515126�0017227�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* Mosh: the mobile shell Copyright 2012 Keith Winstein This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see <http://www.gnu.org/licenses/>. In addition, as a special exception, the copyright holders give permission to link the code of portions of this program with the OpenSSL library under certain conditions as described in each individual source file, and distribute linked combinations including the two. You must obey the GNU General Public License in all respects for all of the code used other than OpenSSL. If you modify file(s) with this exception, you may extend this exception to your version of the file(s), but you are not obligated to do so. If you do not wish to do so, delete this exception statement from your version. If you delete this exception statement from all source files in the program, then also delete it here. */ #include "config.h" #include <string.h> #include <stdlib.h> #include <stdio.h> #include <locale.h> #include <errno.h> #include <string> #if HAVE_LANGINFO_H #include <langinfo.h> #endif #include "locale_utils.h" using namespace std; const string LocaleVar::str( void ) const { if ( name.empty() ) { return string( "[no charset variables]" ); } else { return name + "=" + value; } } const LocaleVar get_ctype( void ) { /* Reimplement the search logic, just for diagnostics */ if ( const char *all = getenv( "LC_ALL" ) ) { return LocaleVar( "LC_ALL", all ); } else if ( const char *ctype = getenv( "LC_CTYPE" ) ) { return LocaleVar( "LC_CTYPE", ctype ); } else if ( const char *lang = getenv( "LANG" ) ) { return LocaleVar( "LANG", lang ); } else { return LocaleVar( "", "" ); } } const char *locale_charset( void ) { static const char ASCII_name[] = "US-ASCII"; /* Produce more pleasant name of US-ASCII */ const char *ret = nl_langinfo( CODESET ); if ( strcmp( ret, "ANSI_X3.4-1968" ) == 0 ) { ret = ASCII_name; } return ret; } bool is_utf8_locale( void ) { /* Verify locale calls for UTF-8 */ if ( strcmp( locale_charset(), "UTF-8" ) != 0 && strcmp( locale_charset(), "utf-8" ) != 0 ) { return 0; } return 1; } void set_native_locale( void ) { /* Adopt native locale */ if ( NULL == setlocale( LC_ALL, "" ) ) { int saved_errno = errno; if ( saved_errno == ENOENT ) { LocaleVar ctype( get_ctype() ); fprintf( stderr, "The locale requested by %s isn't available here.\n", ctype.str().c_str() ); if ( !ctype.name.empty() ) { fprintf( stderr, "Running `locale-gen %s' may be necessary.\n\n", ctype.value.c_str() ); } } else { errno = saved_errno; perror( "setlocale" ); } } } void clear_locale_variables( void ) { unsetenv( "LANG" ); unsetenv( "LANGUAGE" ); unsetenv( "LC_CTYPE" ); unsetenv( "LC_NUMERIC" ); unsetenv( "LC_TIME" ); unsetenv( "LC_COLLATE" ); unsetenv( "LC_MONETARY" ); unsetenv( "LC_MESSAGES" ); unsetenv( "LC_PAPER" ); unsetenv( "LC_NAME" ); unsetenv( "LC_ADDRESS" ); unsetenv( "LC_TELEPHONE" ); unsetenv( "LC_MEASUREMENT" ); unsetenv( "LC_IDENTIFICATION" ); unsetenv( "LC_ALL" ); } �������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������mosh-1.2.5/src/util/locale_utils.h������������������������������������������������������������������0000664�0000000�0000000�00000003577�12550515126�0017077�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* Mosh: the mobile shell Copyright 2012 Keith Winstein This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see <http://www.gnu.org/licenses/>. In addition, as a special exception, the copyright holders give permission to link the code of portions of this program with the OpenSSL library under certain conditions as described in each individual source file, and distribute linked combinations including the two. You must obey the GNU General Public License in all respects for all of the code used other than OpenSSL. If you modify file(s) with this exception, you may extend this exception to your version of the file(s), but you are not obligated to do so. If you do not wish to do so, delete this exception statement from your version. If you delete this exception statement from all source files in the program, then also delete it here. */ #ifndef LOCALE_UTILS_HPP #define LOCALE_UTILS_HPP #include <string> class LocaleVar { public: const std::string name, value; LocaleVar( const char *s_name, const char *s_value ) : name( s_name ), value( s_value ) {} const std::string str( void ) const; }; const LocaleVar get_ctype( void ); const char *locale_charset( void ); bool is_utf8_locale( void ); void set_native_locale( void ); void clear_locale_variables( void ); #endif ���������������������������������������������������������������������������������������������������������������������������������mosh-1.2.5/src/util/pty_compat.cc�������������������������������������������������������������������0000664�0000000�0000000�00000011100�12550515126�0016712�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* Mosh: the mobile shell Copyright 2012 Keith Winstein This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see <http://www.gnu.org/licenses/>. In addition, as a special exception, the copyright holders give permission to link the code of portions of this program with the OpenSSL library under certain conditions as described in each individual source file, and distribute linked combinations including the two. You must obey the GNU General Public License in all respects for all of the code used other than OpenSSL. If you modify file(s) with this exception, you may extend this exception to your version of the file(s), but you are not obligated to do so. If you do not wish to do so, delete this exception statement from your version. If you delete this exception statement from all source files in the program, then also delete it here. */ #include "config.h" #if !defined(HAVE_FORKPTY) || !defined(HAVE_CFMAKERAW) #include <stdio.h> #include <stdlib.h> #include <string.h> #include <unistd.h> #include <fcntl.h> #include <sys/ioctl.h> #include <sys/stropts.h> #include <termios.h> #include "pty_compat.h" #ifndef HAVE_FORKPTY pid_t my_forkpty( int *amaster, char *name, const struct termios *termp, const struct winsize *winp ) { /* For Solaris and AIX */ int master, slave; char *slave_name; pid_t pid; #ifdef _AIX #define PTY_DEVICE "/dev/ptc" #else #define PTY_DEVICE "/dev/ptmx" #endif master = open( PTY_DEVICE, O_RDWR | O_NOCTTY ); if ( master < 0 ) { perror( "open(" PTY_DEVICE ")" ); return -1; } if ( grantpt( master ) < 0 ) { perror( "grantpt" ); close( master ); return -1; } if ( unlockpt(master) < 0 ) { perror( "unlockpt" ); close( master ); return -1; } slave_name = ptsname( master ); if ( slave_name == NULL ) { perror( "ptsname" ); close( master ); return -1; } slave = open( slave_name, O_RDWR | O_NOCTTY ); if ( slave < 0 ) { perror( "open(slave)" ); close( master ); return -1; } #ifndef _AIX if ( ioctl(slave, I_PUSH, "ptem") < 0 || ioctl(slave, I_PUSH, "ldterm") < 0 ) { perror( "ioctl(I_PUSH)" ); close( slave ); close( master ); return -1; } #endif if ( amaster != NULL ) *amaster = master; if ( name != NULL) strcpy( name, slave_name ); if ( termp != NULL ) { if ( tcsetattr( slave, TCSAFLUSH, termp ) < 0 ) { perror( "tcsetattr" ); exit( 1 ); } } // we need to set initial window size, or TIOCGWINSZ fails struct winsize w; w.ws_row = 25; w.ws_col = 80; w.ws_xpixel = 0; w.ws_ypixel = 0; if ( ioctl( slave, TIOCSWINSZ, &w) < 0 ) { perror( "ioctl TIOCSWINSZ" ); exit( 1 ); } if ( winp != NULL ) { if ( ioctl( slave, TIOCGWINSZ, winp ) < 0 ) { perror( "ioctl TIOCGWINSZ" ); exit( 1 ); } } pid = fork(); switch ( pid ) { case -1: /* Error */ perror( "fork()" ); return -1; case 0: /* Child */ if ( setsid() < 0 ) perror( "setsid" ); #ifdef TIOCSCTTY if ( ioctl( slave, TIOCSCTTY, NULL ) < 0 ) { perror( "ioctl" ); return -1; } #else { int dummy_fd; dummy_fd = open (slave_name, O_RDWR); if (dummy_fd < 0) { perror( "open(slave_name)" ); return -1; } close (dummy_fd); } #endif /* TIOCSCTTY */ close( master ); dup2( slave, STDIN_FILENO ); dup2( slave, STDOUT_FILENO ); dup2( slave, STDERR_FILENO ); return 0; default: /* Parent */ close( slave ); return pid; } } #endif #ifndef HAVE_CFMAKERAW void my_cfmakeraw( struct termios *termios_p ) { termios_p->c_iflag &= ~(IGNBRK | BRKINT | PARMRK | ISTRIP | INLCR | IGNCR | ICRNL | IXON); termios_p->c_oflag &= ~OPOST; termios_p->c_lflag &= ~(ECHO | ECHONL | ICANON | ISIG | IEXTEN); termios_p->c_cflag &= ~(CSIZE | PARENB); termios_p->c_cflag |= CS8; termios_p->c_cc[VMIN] = 1; // read() is satisfied after 1 char termios_p->c_cc[VTIME] = 0; // No timer } #endif #endif ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������mosh-1.2.5/src/util/pty_compat.h��������������������������������������������������������������������0000664�0000000�0000000�00000003443�12550515126�0016567�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* Mosh: the mobile shell Copyright 2012 Keith Winstein This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see <http://www.gnu.org/licenses/>. In addition, as a special exception, the copyright holders give permission to link the code of portions of this program with the OpenSSL library under certain conditions as described in each individual source file, and distribute linked combinations including the two. You must obey the GNU General Public License in all respects for all of the code used other than OpenSSL. If you modify file(s) with this exception, you may extend this exception to your version of the file(s), but you are not obligated to do so. If you do not wish to do so, delete this exception statement from your version. If you delete this exception statement from all source files in the program, then also delete it here. */ #ifndef PTY_COMPAT_HPP #define PTY_COMPAT_HPP #include "config.h" #ifndef HAVE_FORKPTY # define forkpty my_forkpty #endif #ifndef HAVE_CFMAKERAW # define cfmakeraw my_cfmakeraw #endif pid_t my_forkpty( int *amaster, char *name, const struct termios *termp, const struct winsize *winp ); void my_cfmakeraw( struct termios *termios_p ); #endif �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������mosh-1.2.5/src/util/select.cc�����������������������������������������������������������������������0000664�0000000�0000000�00000003347�12550515126�0016030�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* Mosh: the mobile shell Copyright 2012 Keith Winstein This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see <http://www.gnu.org/licenses/>. In addition, as a special exception, the copyright holders give permission to link the code of portions of this program with the OpenSSL library under certain conditions as described in each individual source file, and distribute linked combinations including the two. You must obey the GNU General Public License in all respects for all of the code used other than OpenSSL. If you modify file(s) with this exception, you may extend this exception to your version of the file(s), but you are not obligated to do so. If you do not wish to do so, delete this exception statement from your version. If you delete this exception statement from all source files in the program, then also delete it here. */ #include "select.h" fd_set Select::dummy_fd_set; sigset_t Select::dummy_sigset; void Select::handle_signal( int signum ) { fatal_assert( signum >= 0 ); fatal_assert( signum <= MAX_SIGNAL_NUMBER ); Select &sel = get_instance(); sel.got_signal[ signum ] = 1; sel.got_any_signal = 1; } �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������mosh-1.2.5/src/util/select.h������������������������������������������������������������������������0000664�0000000�0000000�00000013210�12550515126�0015660�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* Mosh: the mobile shell Copyright 2012 Keith Winstein This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see <http://www.gnu.org/licenses/>. In addition, as a special exception, the copyright holders give permission to link the code of portions of this program with the OpenSSL library under certain conditions as described in each individual source file, and distribute linked combinations including the two. You must obey the GNU General Public License in all respects for all of the code used other than OpenSSL. If you modify file(s) with this exception, you may extend this exception to your version of the file(s), but you are not obligated to do so. If you do not wish to do so, delete this exception statement from your version. If you delete this exception statement from all source files in the program, then also delete it here. */ #ifndef SELECT_HPP #define SELECT_HPP #include <string.h> #include <errno.h> #include <signal.h> #include <sys/select.h> #include <assert.h> #include "fatal_assert.h" #include "timestamp.h" /* Convenience wrapper for pselect(2). Any signals blocked by calling sigprocmask() outside this code will still be received during Select::select(). So don't do that. */ class Select { public: static Select &get_instance( void ) { /* COFU may or may not be thread-safe, depending on compiler */ static Select instance; return instance; } private: Select() : max_fd( -1 ) , got_any_signal( 0 ) /* These initializations are not used; they are just here to appease -Weffc++. */ , all_fds( dummy_fd_set ) , read_fds( dummy_fd_set ) , error_fds( dummy_fd_set ) , empty_sigset( dummy_sigset ) { FD_ZERO( &all_fds ); FD_ZERO( &read_fds ); FD_ZERO( &error_fds ); clear_got_signal(); fatal_assert( 0 == sigemptyset( &empty_sigset ) ); } void clear_got_signal( void ) { memset( got_signal, 0, sizeof( got_signal ) ); } /* not implemented */ Select( const Select & ); Select &operator=( const Select & ); public: void add_fd( int fd ) { if ( fd > max_fd ) { max_fd = fd; } FD_SET( fd, &all_fds ); } void clear_fds( void ) { FD_ZERO( &all_fds ); } static void add_signal( int signum ) { fatal_assert( signum >= 0 ); fatal_assert( signum <= MAX_SIGNAL_NUMBER ); /* Block the signal so we don't get it outside of pselect(). */ sigset_t to_block; fatal_assert( 0 == sigemptyset( &to_block ) ); fatal_assert( 0 == sigaddset( &to_block, signum ) ); fatal_assert( 0 == sigprocmask( SIG_BLOCK, &to_block, NULL ) ); /* Register a handler, which will only be called when pselect() is interrupted by a (possibly queued) signal. */ struct sigaction sa; sa.sa_flags = 0; sa.sa_handler = &handle_signal; fatal_assert( 0 == sigfillset( &sa.sa_mask ) ); fatal_assert( 0 == sigaction( signum, &sa, NULL ) ); } /* timeout unit: milliseconds; negative timeout means wait forever */ int select( int timeout ) { memcpy( &read_fds, &all_fds, sizeof( read_fds ) ); memcpy( &error_fds, &all_fds, sizeof( error_fds ) ); clear_got_signal(); got_any_signal = 0; #ifdef HAVE_PSELECT struct timespec ts; struct timespec *tsp = NULL; if ( timeout >= 0 ) { ts.tv_sec = timeout / 1000; ts.tv_nsec = 1000000 * (long( timeout ) % 1000); tsp = &ts; } int ret = ::pselect( max_fd + 1, &read_fds, NULL, &error_fds, tsp, &empty_sigset ); #else struct timeval tv; struct timeval *tvp = NULL; sigset_t old_sigset; if ( timeout >= 0 ) { tv.tv_sec = timeout / 1000; tv.tv_usec = 1000 * (long( timeout ) % 1000); tvp = &tv; } int ret = sigprocmask( SIG_SETMASK, &empty_sigset, &old_sigset ); if ( ret != -1 ) { ret = ::select( max_fd + 1, &read_fds, NULL, &error_fds, tvp ); sigprocmask( SIG_SETMASK, &old_sigset, NULL ); } #endif if ( ( ret == -1 ) && ( errno == EINTR ) ) { /* The user should process events as usual. */ FD_ZERO( &read_fds ); FD_ZERO( &error_fds ); ret = 0; } freeze_timestamp(); return ret; } bool read( int fd ) #if FD_ISSET_IS_CONST const #endif { assert( FD_ISSET( fd, &all_fds ) ); return FD_ISSET( fd, &read_fds ); } bool error( int fd ) #if FD_ISSET_IS_CONST const #endif { assert( FD_ISSET( fd, &all_fds ) ); return FD_ISSET( fd, &error_fds ); } bool signal( int signum ) const { fatal_assert( signum >= 0 ); fatal_assert( signum <= MAX_SIGNAL_NUMBER ); return got_signal[ signum ]; } bool any_signal( void ) const { return got_any_signal; } private: static const int MAX_SIGNAL_NUMBER = 64; static void handle_signal( int signum ); int max_fd; /* We assume writes to these ints are atomic, though we also try to mask out concurrent signal handlers. */ int got_any_signal; int got_signal[ MAX_SIGNAL_NUMBER + 1 ]; fd_set all_fds, read_fds, error_fds; sigset_t empty_sigset; static fd_set dummy_fd_set; static sigset_t dummy_sigset; }; #endif ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������mosh-1.2.5/src/util/swrite.cc�����������������������������������������������������������������������0000664�0000000�0000000�00000003760�12550515126�0016065�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* Mosh: the mobile shell Copyright 2012 Keith Winstein This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see <http://www.gnu.org/licenses/>. In addition, as a special exception, the copyright holders give permission to link the code of portions of this program with the OpenSSL library under certain conditions as described in each individual source file, and distribute linked combinations including the two. You must obey the GNU General Public License in all respects for all of the code used other than OpenSSL. If you modify file(s) with this exception, you may extend this exception to your version of the file(s), but you are not obligated to do so. If you do not wish to do so, delete this exception statement from your version. If you delete this exception statement from all source files in the program, then also delete it here. */ #include <unistd.h> #include <string.h> #include <stdio.h> #include "swrite.h" int swrite( int fd, const char *str, ssize_t len ) { ssize_t total_bytes_written = 0; ssize_t bytes_to_write = ( len >= 0 ) ? len : (ssize_t) strlen( str ); while ( total_bytes_written < bytes_to_write ) { ssize_t bytes_written = write( fd, str + total_bytes_written, bytes_to_write - total_bytes_written ); if ( bytes_written <= 0 ) { perror( "write" ); return -1; } else { total_bytes_written += bytes_written; } } return 0; } ����������������mosh-1.2.5/src/util/swrite.h������������������������������������������������������������������������0000664�0000000�0000000�00000003046�12550515126�0015724�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* Mosh: the mobile shell Copyright 2012 Keith Winstein This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see <http://www.gnu.org/licenses/>. In addition, as a special exception, the copyright holders give permission to link the code of portions of this program with the OpenSSL library under certain conditions as described in each individual source file, and distribute linked combinations including the two. You must obey the GNU General Public License in all respects for all of the code used other than OpenSSL. If you modify file(s) with this exception, you may extend this exception to your version of the file(s), but you are not obligated to do so. If you do not wish to do so, delete this exception statement from your version. If you delete this exception statement from all source files in the program, then also delete it here. */ #ifndef SWRITE_HPP #define SWRITE_HPP int swrite( int fd, const char *str, ssize_t len = -1 ); #endif ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������mosh-1.2.5/src/util/timestamp.cc��������������������������������������������������������������������0000664�0000000�0000000�00000005747�12550515126�0016562�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* Mosh: the mobile shell Copyright 2012 Keith Winstein This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see <http://www.gnu.org/licenses/>. In addition, as a special exception, the copyright holders give permission to link the code of portions of this program with the OpenSSL library under certain conditions as described in each individual source file, and distribute linked combinations including the two. You must obey the GNU General Public License in all respects for all of the code used other than OpenSSL. If you modify file(s) with this exception, you may extend this exception to your version of the file(s), but you are not obligated to do so. If you do not wish to do so, delete this exception statement from your version. If you delete this exception statement from all source files in the program, then also delete it here. */ #include "config.h" #include "timestamp.h" #include <errno.h> #if HAVE_CLOCK_GETTIME #include <time.h> #elif HAVE_MACH_ABSOLUTE_TIME #include <mach/mach_time.h> #elif HAVE_GETTIMEOFDAY #include <sys/time.h> #include <stdio.h> #endif static uint64_t millis_cache = -1; uint64_t frozen_timestamp( void ) { if ( millis_cache == uint64_t( -1 ) ) { freeze_timestamp(); } return millis_cache; } void freeze_timestamp( void ) { #if HAVE_CLOCK_GETTIME struct timespec tp; if ( clock_gettime( CLOCK_MONOTONIC, &tp ) < 0 ) { /* did not succeed */ } else { uint64_t millis = tp.tv_nsec / 1000000; millis += uint64_t( tp.tv_sec ) * 1000; millis_cache = millis; return; } #elif HAVE_MACH_ABSOLUTE_TIME static mach_timebase_info_data_t s_timebase_info; static double absolute_to_millis; if (s_timebase_info.denom == 0) { mach_timebase_info(&s_timebase_info); absolute_to_millis = 1e-6 * s_timebase_info.numer / s_timebase_info.denom; } // NB: mach_absolute_time() returns "absolute time units" // We need to apply a conversion to get milliseconds. millis_cache = mach_absolute_time() * absolute_to_millis; return; #elif HAVE_GETTIMEOFDAY // NOTE: If time steps backwards, timeouts may be confused. struct timeval tv; if ( gettimeofday(&tv, NULL) ) { perror( "gettimeofday" ); } else { uint64_t millis = tv.tv_usec / 1000; millis += uint64_t( tv.tv_sec ) * 1000; millis_cache = millis; return; } #else # error "Don't know how to get a timestamp on this platform" #endif } �������������������������mosh-1.2.5/src/util/timestamp.h���������������������������������������������������������������������0000664�0000000�0000000�00000003112�12550515126�0016404�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* Mosh: the mobile shell Copyright 2012 Keith Winstein This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see <http://www.gnu.org/licenses/>. In addition, as a special exception, the copyright holders give permission to link the code of portions of this program with the OpenSSL library under certain conditions as described in each individual source file, and distribute linked combinations including the two. You must obey the GNU General Public License in all respects for all of the code used other than OpenSSL. If you modify file(s) with this exception, you may extend this exception to your version of the file(s), but you are not obligated to do so. If you do not wish to do so, delete this exception statement from your version. If you delete this exception statement from all source files in the program, then also delete it here. */ #ifndef TIMESTAMP_HPP #define TIMESTAMP_HPP #include <stdint.h> void freeze_timestamp( void ); uint64_t frozen_timestamp( void ); #endif ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������