pax_global_header00006660000000000000000000000064146370464740014531gustar00rootroot0000000000000052 comment=a222ea2c9937f4c8785d087ef268a0d4346a96ef sslh-2.1.4/000077500000000000000000000000001463704647400125065ustar00rootroot00000000000000sslh-2.1.4/.github/000077500000000000000000000000001463704647400140465ustar00rootroot00000000000000sslh-2.1.4/.github/workflows/000077500000000000000000000000001463704647400161035ustar00rootroot00000000000000sslh-2.1.4/.github/workflows/container-build.yaml000066400000000000000000000030431463704647400220460ustar00rootroot00000000000000name: Create and publish Container image on: push: branches: - master tags: - 'v*' pull_request: branches: - master env: REGISTRY: ghcr.io IMAGE_NAME: ${{ github.repository }} jobs: build-and-push-image: runs-on: ubuntu-latest permissions: contents: read packages: write steps: - name: Checkout repository uses: actions/checkout@v3 - name: Set up QEMU uses: docker/setup-qemu-action@v2 - name: Set up Docker Buildx uses: docker/setup-buildx-action@v2 - name: Login to Container registry uses: docker/login-action@v2 with: registry: ${{ env.REGISTRY }} username: ${{ github.actor }} password: ${{ secrets.GITHUB_TOKEN }} - name: Docker meta id: meta uses: docker/metadata-action@v4 with: images: | ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }} tags: | type=ref,event=branch type=ref,event=pr type=edge type=semver,pattern={{version}} type=semver,pattern={{major}}.{{minor}} type=semver,pattern={{major}} - name: Build and push uses: docker/build-push-action@v4 with: platforms: linux/amd64,linux/386,linux/arm64,linux/arm/v6,linux/arm/v7 context: . file: Dockerfile push: ${{ github.event_name != 'pull_request' }} tags: ${{ steps.meta.outputs.tags }} labels: ${{ steps.meta.outputs.labels }} sslh-2.1.4/.gitignore000066400000000000000000000002501463704647400144730ustar00rootroot00000000000000*.swp *.swo *.o cscope.* echosrv libsslh.a sslh-fork sslh-select sslh-ev systemd-sslh-generator sslh.8.gz tags version.h /config.status /config.log /config.h /Makefile sslh-2.1.4/COPYING000066400000000000000000000432541463704647400135510ustar00rootroot00000000000000 GNU GENERAL PUBLIC LICENSE Version 2, June 1991 Copyright (C) 1989, 1991 Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA Everyone is permitted to copy and distribute verbatim copies of this license document, but changing it is not allowed. Preamble The licenses for most software are designed to take away your freedom to share and change it. By contrast, the GNU General Public License is intended to guarantee your freedom to share and change free software--to make sure the software is free for all its users. This General Public License applies to most of the Free Software Foundation's software and to any other program whose authors commit to using it. (Some other Free Software Foundation software is covered by the GNU Lesser General Public License instead.) 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 this service 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 make restrictions that forbid anyone to deny you these rights or to ask you to surrender the rights. These restrictions translate to certain responsibilities for you if you distribute copies of the software, or if you modify it. For example, if you distribute copies of such a program, whether gratis or for a fee, you must give the recipients all the rights that you have. 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. We protect your rights with two steps: (1) copyright the software, and (2) offer you this license which gives you legal permission to copy, distribute and/or modify the software. Also, for each author's protection and ours, we want to make certain that everyone understands that there is no warranty for this free software. If the software is modified by someone else and passed on, we want its recipients to know that what they have is not the original, so that any problems introduced by others will not reflect on the original authors' reputations. Finally, any free program is threatened constantly by software patents. We wish to avoid the danger that redistributors of a free program will individually obtain patent licenses, in effect making the program proprietary. To prevent this, we have made it clear that any patent must be licensed for everyone's free use or not licensed at all. The precise terms and conditions for copying, distribution and modification follow. GNU GENERAL PUBLIC LICENSE TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION 0. This License applies to any program or other work which contains a notice placed by the copyright holder saying it may be distributed under the terms of this General Public License. The "Program", below, refers to any such program or work, and a "work based on the Program" means either the Program or any derivative work under copyright law: that is to say, a work containing the Program or a portion of it, either verbatim or with modifications and/or translated into another language. (Hereinafter, translation is included without limitation in the term "modification".) Each licensee is addressed as "you". Activities other than copying, distribution and modification are not covered by this License; they are outside its scope. The act of running the Program is not restricted, and the output from the Program is covered only if its contents constitute a work based on the Program (independent of having been made by running the Program). Whether that is true depends on what the Program does. 1. You may copy and distribute 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 and disclaimer of warranty; keep intact all the notices that refer to this License and to the absence of any warranty; and give any other recipients of the Program a copy of this License along with the Program. You may charge a fee for the physical act of transferring a copy, and you may at your option offer warranty protection in exchange for a fee. 2. You may modify your copy or copies of the Program or any portion of it, thus forming a work based on the Program, and copy and distribute such modifications or work under the terms of Section 1 above, provided that you also meet all of these conditions: a) You must cause the modified files to carry prominent notices stating that you changed the files and the date of any change. b) You must cause any work that you distribute or publish, that in whole or in part contains or is derived from the Program or any part thereof, to be licensed as a whole at no charge to all third parties under the terms of this License. c) If the modified program normally reads commands interactively when run, you must cause it, when started running for such interactive use in the most ordinary way, to print or display an announcement including an appropriate copyright notice and a notice that there is no warranty (or else, saying that you provide a warranty) and that users may redistribute the program under these conditions, and telling the user how to view a copy of this License. (Exception: if the Program itself is interactive but does not normally print such an announcement, your work based on the Program is not required to print an announcement.) These requirements apply to the modified work as a whole. If identifiable sections of that work are not derived from the Program, and can be reasonably considered independent and separate works in themselves, then this License, and its terms, do not apply to those sections when you distribute them as separate works. But when you distribute the same sections as part of a whole which is a work based on the Program, the distribution of the whole must be on the terms of this License, whose permissions for other licensees extend to the entire whole, and thus to each and every part regardless of who wrote it. Thus, it is not the intent of this section to claim rights or contest your rights to work written entirely by you; rather, the intent is to exercise the right to control the distribution of derivative or collective works based on the Program. In addition, mere aggregation of another work not based on the Program with the Program (or with a work based on the Program) on a volume of a storage or distribution medium does not bring the other work under the scope of this License. 3. You may copy and distribute the Program (or a work based on it, under Section 2) in object code or executable form under the terms of Sections 1 and 2 above provided that you also do one of the following: a) Accompany it with the complete corresponding machine-readable source code, which must be distributed under the terms of Sections 1 and 2 above on a medium customarily used for software interchange; or, b) Accompany it with a written offer, valid for at least three years, to give any third party, for a charge no more than your cost of physically performing source distribution, a complete machine-readable copy of the corresponding source code, to be distributed under the terms of Sections 1 and 2 above on a medium customarily used for software interchange; or, c) Accompany it with the information you received as to the offer to distribute corresponding source code. (This alternative is allowed only for noncommercial distribution and only if you received the program in object code or executable form with such an offer, in accord with Subsection b above.) The source code for a work means the preferred form of the work for making modifications to it. For an executable work, complete source code means all the source code for all modules it contains, plus any associated interface definition files, plus the scripts used to control compilation and installation of the executable. However, as a special exception, the source code distributed need not include anything that is normally distributed (in either source or binary form) with the major components (compiler, kernel, and so on) of the operating system on which the executable runs, unless that component itself accompanies the executable. If distribution of executable or object code is made by offering access to copy from a designated place, then offering equivalent access to copy the source code from the same place counts as distribution of the source code, even though third parties are not compelled to copy the source along with the object code. 4. You may not copy, modify, sublicense, or distribute the Program except as expressly provided under this License. Any attempt otherwise to copy, modify, sublicense or distribute the Program is void, and will automatically terminate your rights under this License. However, parties who have received copies, or rights, from you under this License will not have their licenses terminated so long as such parties remain in full compliance. 5. You are not required to accept this License, since you have not signed it. However, nothing else grants you permission to modify or distribute the Program or its derivative works. These actions are prohibited by law if you do not accept this License. Therefore, by modifying or distributing the Program (or any work based on the Program), you indicate your acceptance of this License to do so, and all its terms and conditions for copying, distributing or modifying the Program or works based on it. 6. Each time you redistribute the Program (or any work based on the Program), the recipient automatically receives a license from the original licensor to copy, distribute or modify the Program subject to these terms and conditions. You may not impose any further restrictions on the recipients' exercise of the rights granted herein. You are not responsible for enforcing compliance by third parties to this License. 7. If, as a consequence of a court judgment or allegation of patent infringement or for any other reason (not limited to patent issues), 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 distribute so as to satisfy simultaneously your obligations under this License and any other pertinent obligations, then as a consequence you may not distribute the Program at all. For example, if a patent license would not permit royalty-free redistribution of the Program by all those who receive copies directly or indirectly through you, then the only way you could satisfy both it and this License would be to refrain entirely from distribution of the Program. If any portion of this section is held invalid or unenforceable under any particular circumstance, the balance of the section is intended to apply and the section as a whole is intended to apply in other circumstances. It is not the purpose of this section to induce you to infringe any patents or other property right claims or to contest validity of any such claims; this section has the sole purpose of protecting the integrity of the free software distribution system, which is implemented by public license practices. Many people have made generous contributions to the wide range of software distributed through that system in reliance on consistent application of that system; it is up to the author/donor to decide if he or she is willing to distribute software through any other system and a licensee cannot impose that choice. This section is intended to make thoroughly clear what is believed to be a consequence of the rest of this License. 8. If the distribution and/or use of the Program is restricted in certain countries either by patents or by copyrighted interfaces, the original copyright holder who places the Program under this License may add an explicit geographical distribution limitation excluding those countries, so that distribution is permitted only in or among countries not thus excluded. In such case, this License incorporates the limitation as if written in the body of this License. 9. The Free Software Foundation may publish revised and/or new versions of the 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 a version number of this License which applies to it and "any later version", you have the option of following the terms and conditions either of that version or of any later version published by the Free Software Foundation. If the Program does not specify a version number of this License, you may choose any version ever published by the Free Software Foundation. 10. If you wish to incorporate parts of the Program into other free programs whose distribution conditions are different, write to the author to ask for permission. For software which is copyrighted by the Free Software Foundation, write to the Free Software Foundation; we sometimes make exceptions for this. Our decision will be guided by the two goals of preserving the free status of all derivatives of our free software and of promoting the sharing and reuse of software generally. NO WARRANTY 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, 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. 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR REDISTRIBUTE 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. 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 convey 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 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. Also add information on how to contact you by electronic and paper mail. If the program is interactive, make it output a short notice like this when it starts in an interactive mode: Gnomovision version 69, Copyright (C) year name of author Gnomovision 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, the commands you use may be called something other than `show w' and `show c'; they could even be mouse-clicks or menu items--whatever suits your program. You should also get your employer (if you work as a programmer) or your school, if any, to sign a "copyright disclaimer" for the program, if necessary. Here is a sample; alter the names: Yoyodyne, Inc., hereby disclaims all copyright interest in the program `Gnomovision' (which makes passes at compilers) written by James Hacker. , 1 April 1989 Ty Coon, President of Vice This 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. sslh-2.1.4/ChangeLog000066400000000000000000000326071463704647400142700ustar00rootroot00000000000000v2.1.3: Fix Landlock access to /etc/hosts.deny and /etc/hosts.allow. v2.1.2: Fix inetd mode. v2.1.1: Various minor fixes. v2.1.0: Support for the Landlock LSM. After initial setup, sslh gives up all local file access rights. Reintroduced --ssl as an alias to --tls. Introduce autoconf to adapt to landlock presence. Close connexion without error message if remote client forcefully closes connexion, for Windows. v2.0.1: Fix resolve_on_forward setting, which would crash sslh reliably. v2.0.0: v2.0: New sslh-ev: this is functionally equivalent to sslh-select (mono-process, only forks for specified protocols), but based on libev, which should make it scalable to large numbers of connections. New log system: instead of --verbose with arbitrary levels, there are now several message classes. Each message class can be set to go to stderr, syslog, or both. Classes are documented in example.cfg. UDP connections are now managed in a hash to avoid linear searches. The downside is that the number of UDP connections is a hard limit, configurable with the 'udp_max_connections', which defaults to 1024. Timeouts are managed with lists. inetd merges stderr output to what is sent to the client, which is a security issue as it might give information to an attacker. When inetd is activated, stderr is forcibly closed. New protocol-level option `resolve_on_forward`, requests that target names are resolved at each connection instead of at startup. Useful for dynamic DNS situations. (Paul Schroeder/milkpirate) New probe for MSRDP (akappner). v1.22: 17AUG2021 sslh-select now supports UDP protocols. Probes specified in the `protocols` configuration entry are tried on incoming packets, TCP or UDP, and forwarded based on the input protocol (an incoming TCP connection will be forwarded as TCP, and same with UDP). This has been tested with DNS as shown in udp.cfg: incoming packets that contain my domain name are assumed to be a DNS request and forwarded accordingly. Note this could cause problems if combined with incoming TLS with SNI. UDP clients and servers need to agree on the IPv4/IPv6 they use: use the same protocol on all sides! Often, this means explicitly using 'ip4-localhost'. UDP sender-receiver pairs (connections, so to speak) are kept for 60s, which can be changed with `udp_timeout` in the configuration. Added probes for UDP protocols QUICK and Teamspeak. Added probes for syslog protocol. sslh-select refactored to change linear searches through connections to linear searches through fd_set. Fixed a libconfig call to support libconfig 1.7.3. Added symbol to support libconfig 1.4.9, still in use in CentOS7. Warn about unknown settings in the configuration file. Added per-protocol `transparent` option. sslh-fork drops the capability after creating the server-side transparent socket. Transparent now uses CAP_NET_RAW instead of CAP_NET_ADMIN. Removed compile-time option to use POSIX regex. Now regex must be PCRE2 (Perl-Compatible). This was in fact the case since v1.21, as PCRE are used to parse the config file. v1.21: 11JUL2020 WARNING: Moved configuration and command-line management to use conf2struct. Changes are: * `--ssl` and using `name: 'ssl'` in config file is no longer supported, use `tls` instead. * command line option <-F|--config> no longer defaults to /etc/sslh.cfg, so you have to specify it explicitly. * command line option <-v|--verbose> takes a mandatory integer parameter Added TCP_FASTOPEN support for client sockets (if tfo_ok is specified in their configuration) and for listening socket, if all client protocols support it. (Craig Andrews) Added 'minlength' option to skip a probe if less than that many bytes have been received (mostly for regex) Update Let's Encrypt entry in example.cfg for tls-alpn-01 challenges; tls-sni-* challenges are now deprecated. Log to syslog even if in foreground (for people who use fail2ban) Use syslog_facility: "none" to disable syslog output. Changed exit code for illegal command line parameter from 1 to 6 (for testing purposes) v1.20: 20NOV2018 Added support for socks5 protocol (Eugene Protozanov) New probing method: Before, probes were tried in order, repeating on the same probe as long it returned PROBE_AGAIN before moving to the next one. This means a probe which requires a lot of data (i.e. return PROBE_AGAIN for a long time) could prevent successful matches from subsequent probes. The configuration file needed to take that into account. Now, all probes are tried each time new data is found. If any probe matches, use it. If at least one probe requires more data, wait for more. If all probes failed, connect to the last one. So the only thing to know when writing the configuration file is that 'anyprot' needs to be last. Test suite heavily refactored; `t` uses `test.cfg` to decide which probes to test and all setup is automatic; probes get tested with 'fast' (entire first message in one packet) and 'slow' (one byte at a time); when SNI/ALPN are defined, all combinations are tested. Old 'tls' probe removed, 'sni_alpn' probe renamed as 'tls'. You'll need to change 'sni_alpn' to 'tls' in your configuration file, if ever you used it. v1.19: 20JAN2018 Added 'syslog_facility' configuration option to specify where to log. TLS now supports SNI and ALPN (Travis Burtrum), including support for Let's Encrypt challenges (Jonathan McCrohan) ADB probe. (Mike Frysinger) Added per-protocol 'fork' option. (Oleg Oshmyan) Added chroot option. (Mike Frysinger) A truckload of bug fixes and documentation improvements (Various contributors) v1.18: 29MAR2016 Added USELIBPCRE to make use of regex engine optional. Added support for RFC4366 SNI and RFC7301 ALPN (Travis Burtrum) Changed connection log to include the name of the probe that triggered. Changed configuration file format: 'probe' field is no longer required, 'name' field can now contain 'tls' or 'regex', with corresponding options (see example.cfg) Added 'log_level' option to each protocol, which allows to turn off generation of log at each connection. Added 'keepalive' option. v1.17: 09MAR2015 Support RFC5952-style IPv6 addresses, e.g. [::]:443. Transparent proxy support for FreeBSD. (Ruben van Staveren) Using -F with no argument will try /etc/sslh/sslh.cfg and then /etc/sslh.cfg as configuration files. (argument to -F can no longer be separated from the option by a space, e.g. must be -Ffoo.cfg) Call setgroups() before setgid() (fixes potential privilege escalation). (Lars Vogdt) Use portable way of getting modified time for OSX support. (Aaron Madlon-Kay) Example configuration for fail2ban. (Every Mouw) v1.16: 11FEB2014 Probes made more resilient, to incoming data containing NULLs. Also made them behave properly when receiving too short packets to probe on the first incoming packet. (Ondrej Kuznk) Libcap support: Keep only CAP_NET_ADMIN if started as root with transparent proxying and dropping privileges (enable USELIBCAP in Makefile). This avoids having to mess with filesystem capabilities. (Sebastian Schmidt/yath) Fixed bugs related to getpeername that would cause sslh to quit erroneously (getpeername can return actual errors if connections are dropped before getting to getpeername). Set IP_FREEBIND if available to bind to addresses that don't yet exist. v1.15: 27JUL2013 Added --transparent option for transparent proxying. See README for iptables magic and capability management. Fixed bug in sslh-select: if number of opened file descriptor became bigger than FD_SETSIZE, bad things would happen. Fixed bug in sslh-select: if socket dropped while deferred_data was present, sslh-select would crash. Increased FD_SETSIZE for Cygwin, as the default 64 is too low for even moderate load. v1.14: 21DEC2012 Corrected OpenVPN probe to support pre-shared secret mode (OpenVPN port-sharing code is... wrong). Thanks to Kai Ellinger for help in investigating and testing. Added an actual TLS/SSL probe. Added configurable --on-timeout protocol specification. Added a --anyprot protocol probe (equivalent to what --ssl was). Makefile respects the user's compiler and CFLAG choices (falling back to the current values if undefined), as well as LDFLAGS. (Michael Palimaka) Added "After" and "KillMode" to systemd.sslh.service (Thomas Weischuh). Added LSB tags to etc.init.d.sslh (Thomas Varis). v1.13: 18MAY2012 Write PID file before dropping privileges. Added --background, which overrides 'foreground' configuration file setting. Added example systemd service file from Archlinux in scripts/ https://projects.archlinux.org/svntogit/community.git/tree/trunk/sslh.service?h=packages/sslh (Sbastien Luttringer) v1.12: 08MAY2012 Added support for configuration file. New protocol probes can be defined using regular expressions that match the first packet sent by the client. sslh now connects timed out connections to the first configured protocol instead of 'ssh' (just make sure ssh is the first defined protocol). sslh now tries protocols in the order in which they are defined (just make sure sslh is the last defined protocol). v1.11: 21APR2012 WARNING: defaults have been removed for --user and --pidfile options, update your start-up scripts! No longer stop sslh when reverse DNS requests fail for logging. Added HTTP probe. No longer create new session if running in foreground. No longer default to changing user to 'nobody'. If --user isn't specified, just run as current user. No longer create PID file by default, it should be explicitly set with --pidfile. No longer log to syslog if in foreground. Logs are instead output to stderr. The four changes above make it straightforward to integrate sslh with systemd, and should help with launchd. v1.10: 27NOV2011 Fixed calls referring to sockaddr length so they work with FreeBSD. Try target addresses in turn until one works if there are several (e.g. "localhost:22" resolves to an IPv6 address and an IPv4 address and sshd does not listen on IPv6). Fixed sslh-fork so killing the head process kills the listener processes. Heavily cleaned up test suite. Added stress test t_load script. Added coverage (requires lcov). Support for XMPP (Arnaud Gendre). Updated README.MacOSX (Aaron Madlon-Kay). v1.9: 02AUG2011 WARNING: This version does not work with FreeBSD and derivatives! WARNING: Options changed, you'll need to update your start-up scripts! Log format changed, you'll need to update log processing scripts! Now supports IPv6 throughout (both on listening and forwarding) Logs now contain IPv6 addresses, local forwarding address, and resolves names (unless --numeric is specified). Introduced long options. Options -l, -s and -o replaced by their long counterparts. Defaults for SSL and SSH options suppressed (it's legitimate to want to use sslh to mux OpenVPN and tinc while not caring about SSH nor SSL). Bind to multiple addresses with multiple -p options. Support for tinc VPN (experimental). Numeric logging option. v1.8: 15JUL2011 Changed log format to make it possible to link connections to subsequent logs from other services. Updated CentOS init.d script (Andre Krajnik). Fixed zombie issue with OpenBSD (The SA_NOCLDWAIT flag is not propagated to the child process, so we set up signals after the fork.) (Franois FRITZ) Added -o "OpenVPN" and OpenVPN probing and support. Added single-threaded, select(2)-based version. Added support for "Bold" SSH clients (clients that speak first) Thanks to Guillaume Ricaud for spotting a regression bug. Added -f "foreground" option. Added test suite. (only tests connexions. No test for libwrap, setsid, setuid and so on) and corresponding 'make test' target. Added README.MacOSX (thanks Aaron Madlon-Kay) Documented use with proxytunnel and corkscrew in README. v1.7: 01FEB2010 Added CentOS init.d script (Andre Krajnik). Fixed default ssl address inconsistency, now defaults to "localhost:443" and fixed documentation accordingly (pointed by Markus Schalke). Children no longer bind to the listen socket, so parent server can be stopped without killing an active child (pointed by Matthias Buecher). Inetd support (Dima Barsky). v1.6: 25APR2009 Added -V, version option. Install target directory configurable in Makefile Changed syslog prefix in auth.log to "sslh[%pid]" Man page new 'make install' and 'make install-debian' targets PID file now specified using -P command line option Actually fixed zombie generation (the v1.5 patch got lost, doh!) v1.5: 10DEC2008 Fixed zombie generation. Added support scripts (), Makefile. Changed all 'connexions' to 'connections' to please pesky users. Damn users. v1.4: 13JUL2008 Added libwrap support for ssh service (Christian Weinberger) Only SSH is libwraped, not SSL. v1.3: 14MAY2008 Added parsing for local interface to listen on Changed default SSL connection to port 442 (443 doesn't make sense as a default as we're already listening on 443) Syslog incoming connections v1.2: 12MAY2008 Fixed compilation warning for AMD64 (Thx Daniel Lange) v1.1: 21MAY2007 Making sslhc more like a real daemon: * If $PIDFILE is defined, write first PID to it upon startup * Fork at startup (detach from terminal) (thanks to http://www.enderunix.org/docs/eng/daemon.php -- good checklist) * Less memory usage (?) v1.0: Basic functionality: privilege dropping, target hostnames and ports configurable. sslh-2.1.4/Dockerfile000066400000000000000000000015041463704647400145000ustar00rootroot00000000000000ARG ALPINE_VERSION="latest" ARG TARGET_ARCH="library" FROM docker.io/${TARGET_ARCH}/alpine:${ALPINE_VERSION} AS build WORKDIR /sslh RUN apk add --no-cache \ 'gcc' \ 'libconfig-dev' \ 'make' \ 'musl-dev' \ 'pcre2-dev' \ 'perl' \ ; COPY . /sslh RUN ./configure && make sslh-select && strip sslh-select FROM docker.io/${TARGET_ARCH}/alpine:${ALPINE_VERSION} COPY --from=build "/sslh/sslh-select" "/usr/local/bin/sslh" RUN apk add --no-cache \ 'libconfig' \ 'pcre2' \ 'iptables' \ 'ip6tables' \ 'libcap' \ && \ adduser -s '/bin/sh' -S -D sslh && \ setcap cap_net_bind_service,cap_net_raw+ep /usr/local/bin/sslh COPY "./container-entrypoint.sh" "/init" ENTRYPOINT [ "/init" ] # required for updating iptables USER root:root sslh-2.1.4/Makefile.in000066400000000000000000000114621463704647400145570ustar00rootroot00000000000000 VERSION=$(shell ./genver.sh -r) # Configuration -- you probably need to `make clean` if you # change any of these # landlock is checked by `configure` and set in config.h ENABLE_SANITIZER= # Enable ASAN/LSAN/UBSAN ENABLE_REGEX=1 # Enable regex probes USELIBCONFIG=1 # Use libconfig? (necessary to use configuration files) USELIBEV=1 # Use libev? USELIBWRAP?= # Use libwrap? USELIBCAP= # Use libcap? USESYSTEMD= # Make use of systemd socket activation USELIBBSD?= # Use libbsd (needed to update process name in `ps`) COV_TEST= # Perform test coverage? PREFIX?=/usr BINDIR?=$(PREFIX)/sbin MANDIR?=$(PREFIX)/share/man/man8 MAN=sslh.8.gz # man page name # End of configuration -- the rest should take care of # itself ifneq ($(strip $(ENABLE_SANITIZER)),) CFLAGS_SAN=-fsanitize=address -fsanitize=leak -fsanitize=undefined endif ifneq ($(strip $(COV_TEST)),) CFLAGS_COV=-fprofile-arcs -ftest-coverage endif CC ?= gcc AR ?= ar CFLAGS +=-Wall -O2 -DLIBPCRE -g $(CFLAGS_COV) $(CFLAGS_SAN) LIBS=-lm -lpcre2-8 OBJS=sslh-conf.o common.o log.o sslh-main.o probe.o tls.o argtable3.o collection.o gap.o tcp-probe.o landlock.o OBJS_A=libsslh.a FORK_OBJS=sslh-fork.o $(OBJS_A) SELECT_OBJS=processes.o udp-listener.o sslh-select.o hash.o tcp-listener.o $(OBJS_A) EV_OBJS=processes.o udp-listener.o sslh-ev.o hash.o tcp-listener.o $(OBJS_A) CONDITIONAL_TARGETS= ifneq ($(strip $(USELIBWRAP)),) LIBS:=$(LIBS) -lwrap CPPFLAGS+=-DLIBWRAP endif ifneq ($(strip $(ENABLE_REGEX)),) CPPFLAGS+=-DENABLE_REGEX endif ifneq ($(strip $(USELIBCONFIG)),) LIBS:=$(LIBS) -lconfig CPPFLAGS+=-DLIBCONFIG endif ifneq ($(strip $(USELIBCAP)),) LIBS:=$(LIBS) -lcap CPPFLAGS+=-DLIBCAP endif ifneq ($(strip $(USESYSTEMD)),) LIBS:=$(LIBS) -lsystemd CPPFLAGS+=-DSYSTEMD CONDITIONAL_TARGETS+=systemd-sslh-generator endif ifneq ($(strip $(USELIBBSD)),) LIBS:=$(LIBS) -lbsd CPPFLAGS+=-DLIBBSD endif ifneq ($(strip $(USELIBEV)),) CONDITIONAL_TARGETS+=sslh-ev endif all: sslh-fork sslh-select $(MAN) echosrv $(CONDITIONAL_TARGETS) %.o: %.c %.h version.h $(CC) $(CFLAGS) $(CPPFLAGS) -c $< -o $@ $(OBJS_A): $(OBJS) $(AR) rcs $(OBJS_A) $(OBJS) version.h: ./genver.sh >version.h $(OBJS) $(FORK_OBJS) $(SELECT_OBJS) $(EV_OBJS): argtable3.h collection.h common.h gap.h hash.h log.h probe.h processes.h sslh-conf.h tcp-listener.h tcp-probe.h tls.h udp-listener.h version.h c2s: conf2struct sslhconf.cfg conf2struct echosrv.cfg sslh-conf.c sslh-conf.h: sslhconf.cfg $(warning "sslhconf.cfg is more recent than sslh-conf.[ch]. Use `make c2s` to rebuild using `conf2struct`") sslh-fork: version.h Makefile $(FORK_OBJS) $(CC) $(CFLAGS) $(LDFLAGS) -o sslh-fork $(FORK_OBJS) $(LIBS) sslh-select: version.h $(SELECT_OBJS) Makefile $(CC) $(CFLAGS) $(LDFLAGS) -o sslh-select $(SELECT_OBJS) $(LIBS) sslh-ev: version.h $(EV_OBJS) Makefile $(CC) $(CFLAGS) $(LDFLAGS) -o sslh-ev $(EV_OBJS) $(LIBS) -lev systemd-sslh-generator: systemd-sslh-generator.o $(CC) $(CFLAGS) $(LDFLAGS) -o systemd-sslh-generator systemd-sslh-generator.o -lconfig echosrv-conf.c echosrv-conf.h: echosrv.cfg $(warning "echosrv.cfg is more recent than echosrv-conf.[ch]. Use `make c2s` to rebuild using `conf2struct`") echosrv: version.h echosrv-conf.c echosrv.o echosrv-conf.o argtable3.o $(CC) $(CFLAGS) $(LDFLAGS) -o echosrv echosrv.o echosrv-conf.o argtable3.o $(LIBS) landlock.o: config.h $(MAN): sslh.pod Makefile pod2man --section=8 --release=$(VERSION) --center=" " sslh.pod | gzip -9 - > $(MAN) # Create release: export clean tree and tag current # configuration release: git archive master --prefix="sslh-$(VERSION)/" | gzip > /tmp/sslh-$(VERSION).tar.gz gpg --detach-sign --armor /tmp/sslh-$(VERSION).tar.gz # Build docker image docker: docker image build -t "sslh:${VERSION}" . docker image tag "sslh:${VERSION}" sslh:latest docker-clean: yes | docker image rm "sslh:${VERSION}" sslh:latest yes | docker image prune # generic install: install binary and man page install: sslh-fork $(MAN) mkdir -p $(DESTDIR)/$(BINDIR) mkdir -p $(DESTDIR)/$(MANDIR) install -p sslh-fork $(DESTDIR)/$(BINDIR)/sslh install -p -m 0644 $(MAN) $(DESTDIR)/$(MANDIR)/$(MAN) # "extended" install for Debian: install startup script install-debian: install sslh $(MAN) sed -e "s+^PREFIX=+PREFIX=$(PREFIX)+" scripts/etc.init.d.sslh > /etc/init.d/sslh chmod 755 /etc/init.d/sslh update-rc.d sslh defaults uninstall: rm -f $(DESTDIR)$(BINDIR)/sslh $(DESTDIR)$(MANDIR)/$(MAN) $(DESTDIR)/etc/init.d/sslh $(DESTDIR)/etc/default/sslh update-rc.d sslh remove distclean: clean rm -f tags sslh-conf.[ch] echosrv-conf.[ch] cscope.* clean: rm -f sslh-fork sslh-select $(CONDITIONAL_TARGETS) echosrv version.h $(MAN) systemd-sslh-generator *.o *.gcov *.gcno *.gcda *.png *.html *.css *.info tags: ctags --globals -T *.[ch] cscope: -find . -name "*.[chS]" >cscope.files -cscope -b -R test: ./t sslh-2.1.4/README.md000066400000000000000000000121101463704647400137600ustar00rootroot00000000000000sslh -- A ssl/ssh multiplexer ============================= `sslh` accepts connections on specified ports, and forwards them further based on tests performed on the first data packet sent by the remote client. Probes for HTTP, TLS/SSL (including SNI and ALPN), SSH, OpenVPN, tinc, XMPP, SOCKS5, are implemented, and any other protocol that can be tested using a regular expression, can be recognised. A typical use case is to allow serving several services on port 443 (e.g. to connect to SSH from inside a corporate firewall, which almost never block port 443) while still serving HTTPS on that port. Hence `sslh` acts as a protocol demultiplexer, or a switchboard. With the SNI and ALPN probe, it makes a good front-end to a virtual host farm hosted behind a single IP address. `sslh` has the bells and whistles expected from a mature daemon: privilege and capabilities dropping, inetd support, systemd support, transparent proxying, chroot, logging, IPv4 and IPv6, TCP and UDP, a fork-based, a select-based model, and yet another based on libev for larger installations. Install ======= Please refer to the [install guide](doc/INSTALL.md). Configuration ============= Please refer to the [configuration guide](doc/config.md). Transparent proxying -------------------- Transparent proxying allows the target server to see the original client IP address, i.e. `sslh` becomes invisible. This means services behind `sslh` (Apache, `sshd` and so on) will see the external IP and ports as if the external world connected directly to them. This simplifies IP-based access control (or makes it possible at all), and makes it possible to use IP-based banning tools such as `fail2ban`. There are two methods. One uses additional virtual network interfaces. The principle and basic setup is described [here](doc/simple_transparent_proxy.md), with further scenarios described [there](doc/scenarios-for-simple-transparent-proxy.md). Another method uses iptable packet marking features, and is highly dependent on your network environment and infrastructure setup. There is no known generic approach, and if you do not find directions for your exact setup, you will probably need an extensive knowledge of network management and iptables setup". It is described in its own [document](doc/tproxy.md). In most cases, you will be better off following the first method. Docker image ------------ How to use --- ```bash docker run \ --cap-add CAP_NET_RAW \ --cap-add CAP_NET_BIND_SERVICE \ --rm \ -it \ ghcr.io/yrutschle/sslh:latest \ --foreground \ --listen=0.0.0.0:443 \ --ssh=hostname:22 \ --tls=hostname:443 ``` docker-compose example ```yaml version: "3" services: sslh: image: ghcr.io/yrutschle/sslh:latest hostname: sslh ports: - 443:443 command: --foreground --listen=0.0.0.0:443 --tls=nginx:443 --openvpn=openvpn:1194 depends_on: - nginx - openvpn nginx: image: nginx openvpn: image: openvpn ``` Transparent mode 1: using sslh container for networking _Note: For transparent mode to work, the sslh container must be able to reach your services via **localhost**_ ```yaml version: "3" services: sslh: build: https://github.com/yrutschle/sslh.git container_name: sslh environment: - TZ=${TZ} cap_add: - NET_ADMIN - NET_RAW - NET_BIND_SERVICE sysctls: - net.ipv4.conf.default.route_localnet=1 - net.ipv4.conf.all.route_localnet=1 command: --transparent --foreground --listen=0.0.0.0:443 --tls=localhost:8443 --openvpn=localhost:1194 ports: - 443:443 #sslh - 80:80 #nginx - 8443:8443 #nginx - 1194:1194 #openvpn extra_hosts: - localbox:host-gateway restart: unless-stopped nginx: image: nginx:latest ..... network_mode: service:sslh #set nginx container to use sslh networking. # ^^^ This is required. This makes nginx reachable by sslh via localhost openvpn: image: openvpn:latest ..... network_mode: service:sslh #set openvpn container to use sslh networking ``` Transparent mode 2: using host networking ```yaml version: "3" services: sslh: build: https://github.com/yrutschle/sslh.git container_name: sslh environment: - TZ=${TZ} cap_add: - NET_ADMIN - NET_RAW - NET_BIND_SERVICE # must be set manually #sysctls: # - net.ipv4.conf.default.route_localnet=1 # - net.ipv4.conf.all.route_localnet=1 command: --transparent --foreground --listen=0.0.0.0:443 --tls=localhost:8443 --openvpn=localhost:1194 network_mode: host restart: unless-stopped nginx: image: nginx:latest ..... ports: - 8443:8443 # bind to docker host on port 8443 openvpn: image: openvpn:latest ..... ports: - 1194:1194 # bind to docker host on port 1194 ``` Comments? Questions? ==================== You can subscribe to the `sslh` mailing list here: This mailing list should be used for discussion, feature requests, and will be the preferred channel for announcements. Of course, check the [FAQ](doc/FAQ.md) first! sslh-2.1.4/TODO000066400000000000000000000017221463704647400132000ustar00rootroot00000000000000Here's a list of features that have been suggested or sometimes requested. This list is not a roadmap and shouldn't be construed to mean that any of this will happen. - configurable behaviour depending on services (e.g. select() for ssl but fork() for ssh). - have certain services available only from specified subnets - some sort of "service knocking" allowing to activate a service upon some external even, similar to port knocking; for example, go to a specific URL to enable sslh forwarding to sshd for a set period of time: * sslh listens on 443 and only directs to httpd * user goes somewhere to https://example.org/open_ssh.cgi * open_ssh.cgi tells sslh * sslh starts checking if incoming connections are ssh, and if they are, forward to sshd * 10 minutes later, sslh stops forwarding to ssh That would make it almost impossible for an observer (someone who'd telnet regularly on 443) to ever notice both services are available on 443. sslh-2.1.4/argtable3.c000066400000000000000000006264641463704647400145400ustar00rootroot00000000000000/******************************************************************************* * This file is part of the argtable3 library. * * Copyright (C) 1998-2001,2003-2011,2013 Stewart Heitmann * * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * * Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * Neither the name of STEWART HEITMANN nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL STEWART HEITMANN BE LIABLE FOR ANY DIRECT, * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ******************************************************************************/ #include "argtable3.h" #define ARG_AMALGAMATION /******************************************************************************* * argtable3_private: Declares private types, constants, and interfaces * * This file is part of the argtable3 library. * * Copyright (C) 2013-2019 Tom G. Huang * * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * * Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * Neither the name of STEWART HEITMANN nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL STEWART HEITMANN BE LIABLE FOR ANY DIRECT, * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ******************************************************************************/ #ifndef ARG_UTILS_H #define ARG_UTILS_H #include #define ARG_ENABLE_TRACE 0 #define ARG_ENABLE_LOG 1 #ifdef __cplusplus extern "C" { #endif enum { ARG_ERR_MINCOUNT = 1, ARG_ERR_MAXCOUNT, ARG_ERR_BADINT, ARG_ERR_OVERFLOW, ARG_ERR_BADDOUBLE, ARG_ERR_BADDATE, ARG_ERR_REGNOMATCH }; typedef void(arg_panicfn)(const char* fmt, ...); #if defined(_MSC_VER) #define ARG_TRACE(x) \ __pragma(warning(push)) __pragma(warning(disable : 4127)) do { \ if (ARG_ENABLE_TRACE) \ dbg_printf x; \ } \ while (0) \ __pragma(warning(pop)) #define ARG_LOG(x) \ __pragma(warning(push)) __pragma(warning(disable : 4127)) do { \ if (ARG_ENABLE_LOG) \ dbg_printf x; \ } \ while (0) \ __pragma(warning(pop)) #else #define ARG_TRACE(x) \ do { \ if (ARG_ENABLE_TRACE) \ dbg_printf x; \ } while (0) #define ARG_LOG(x) \ do { \ if (ARG_ENABLE_LOG) \ dbg_printf x; \ } while (0) #endif /* * Rename a few generic names to unique names. * They can be a problem for the platforms like NuttX, where * the namespace is flat for everything including apps and libraries. */ #define xmalloc argtable3_xmalloc #define xcalloc argtable3_xcalloc #define xrealloc argtable3_xrealloc #define xfree argtable3_xfree extern void dbg_printf(const char* fmt, ...); extern void arg_set_panic(arg_panicfn* proc); extern void* xmalloc(size_t size); extern void* xcalloc(size_t count, size_t size); extern void* xrealloc(void* ptr, size_t size); extern void xfree(void* ptr); struct arg_hashtable_entry { void *k, *v; unsigned int h; struct arg_hashtable_entry* next; }; typedef struct arg_hashtable { unsigned int tablelength; struct arg_hashtable_entry** table; unsigned int entrycount; unsigned int loadlimit; unsigned int primeindex; unsigned int (*hashfn)(const void* k); int (*eqfn)(const void* k1, const void* k2); } arg_hashtable_t; /** * @brief Create a hash table. * * @param minsize minimum initial size of hash table * @param hashfn function for hashing keys * @param eqfn function for determining key equality * @return newly created hash table or NULL on failure */ arg_hashtable_t* arg_hashtable_create(unsigned int minsize, unsigned int (*hashfn)(const void*), int (*eqfn)(const void*, const void*)); /** * @brief This function will cause the table to expand if the insertion would take * the ratio of entries to table size over the maximum load factor. * * This function does not check for repeated insertions with a duplicate key. * The value returned when using a duplicate key is undefined -- when * the hash table changes size, the order of retrieval of duplicate key * entries is reversed. * If in doubt, remove before insert. * * @param h the hash table to insert into * @param k the key - hash table claims ownership and will free on removal * @param v the value - does not claim ownership * @return non-zero for successful insertion */ void arg_hashtable_insert(arg_hashtable_t* h, void* k, void* v); #define ARG_DEFINE_HASHTABLE_INSERT(fnname, keytype, valuetype) \ int fnname(arg_hashtable_t* h, keytype* k, valuetype* v) { return arg_hashtable_insert(h, k, v); } /** * @brief Search the specified key in the hash table. * * @param h the hash table to search * @param k the key to search for - does not claim ownership * @return the value associated with the key, or NULL if none found */ void* arg_hashtable_search(arg_hashtable_t* h, const void* k); #define ARG_DEFINE_HASHTABLE_SEARCH(fnname, keytype, valuetype) \ valuetype* fnname(arg_hashtable_t* h, keytype* k) { return (valuetype*)(arg_hashtable_search(h, k)); } /** * @brief Remove the specified key from the hash table. * * @param h the hash table to remove the item from * @param k the key to search for - does not claim ownership */ void arg_hashtable_remove(arg_hashtable_t* h, const void* k); #define ARG_DEFINE_HASHTABLE_REMOVE(fnname, keytype, valuetype) \ void fnname(arg_hashtable_t* h, keytype* k) { arg_hashtable_remove(h, k); } /** * @brief Return the number of keys in the hash table. * * @param h the hash table * @return the number of items stored in the hash table */ unsigned int arg_hashtable_count(arg_hashtable_t* h); /** * @brief Change the value associated with the key. * * function to change the value associated with a key, where there already * exists a value bound to the key in the hash table. * Source due to Holger Schemel. * * @name hashtable_change * @param h the hash table * @param key * @param value */ int arg_hashtable_change(arg_hashtable_t* h, void* k, void* v); /** * @brief Free the hash table and the memory allocated for each key-value pair. * * @param h the hash table * @param free_values whether to call 'free' on the remaining values */ void arg_hashtable_destroy(arg_hashtable_t* h, int free_values); typedef struct arg_hashtable_itr { arg_hashtable_t* h; struct arg_hashtable_entry* e; struct arg_hashtable_entry* parent; unsigned int index; } arg_hashtable_itr_t; arg_hashtable_itr_t* arg_hashtable_itr_create(arg_hashtable_t* h); void arg_hashtable_itr_destroy(arg_hashtable_itr_t* itr); /** * @brief Return the value of the (key,value) pair at the current position. */ extern void* arg_hashtable_itr_key(arg_hashtable_itr_t* i); /** * @brief Return the value of the (key,value) pair at the current position. */ extern void* arg_hashtable_itr_value(arg_hashtable_itr_t* i); /** * @brief Advance the iterator to the next element. Returns zero if advanced to end of table. */ int arg_hashtable_itr_advance(arg_hashtable_itr_t* itr); /** * @brief Remove current element and advance the iterator to the next element. */ int arg_hashtable_itr_remove(arg_hashtable_itr_t* itr); /** * @brief Search and overwrite the supplied iterator, to point to the entry matching the supplied key. * * @return Zero if not found. */ int arg_hashtable_itr_search(arg_hashtable_itr_t* itr, arg_hashtable_t* h, void* k); #define ARG_DEFINE_HASHTABLE_ITERATOR_SEARCH(fnname, keytype) \ int fnname(arg_hashtable_itr_t* i, arg_hashtable_t* h, keytype* k) { return (arg_hashtable_iterator_search(i, h, k)); } #ifdef __cplusplus } #endif #endif /******************************************************************************* * arg_utils: Implements memory, panic, and other utility functions * * This file is part of the argtable3 library. * * Copyright (C) 2013-2019 Tom G. Huang * * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * * Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * Neither the name of STEWART HEITMANN nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL STEWART HEITMANN BE LIABLE FOR ANY DIRECT, * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ******************************************************************************/ #include "argtable3.h" #ifndef ARG_AMALGAMATION #include "argtable3_private.h" #endif #include #include #include #include static void panic(const char* fmt, ...); static arg_panicfn* s_panic = panic; void dbg_printf(const char* fmt, ...) { va_list args; va_start(args, fmt); vfprintf(stderr, fmt, args); va_end(args); } static void panic(const char* fmt, ...) { va_list args; char* s; va_start(args, fmt); vfprintf(stderr, fmt, args); va_end(args); #if defined(_MSC_VER) #pragma warning(push) #pragma warning(disable : 4996) #endif s = getenv("EF_DUMPCORE"); #if defined(_MSC_VER) #pragma warning(pop) #endif if (s != NULL && *s != '\0') { abort(); } else { exit(EXIT_FAILURE); } } void arg_set_panic(arg_panicfn* proc) { s_panic = proc; } void* xmalloc(size_t size) { void* ret = malloc(size); if (!ret) { s_panic("Out of memory!\n"); } return ret; } void* xcalloc(size_t count, size_t size) { size_t allocated_count = count && size ? count : 1; size_t allocated_size = count && size ? size : 1; void* ret = calloc(allocated_count, allocated_size); if (!ret) { s_panic("Out of memory!\n"); } return ret; } void* xrealloc(void* ptr, size_t size) { size_t allocated_size = size ? size : 1; void* ret = realloc(ptr, allocated_size); if (!ret) { s_panic("Out of memory!\n"); } return ret; } void xfree(void* ptr) { free(ptr); } static void merge(void* data, int esize, int i, int j, int k, arg_comparefn* comparefn) { char* a = (char*)data; char* m; int ipos, jpos, mpos; /* Initialize the counters used in merging. */ ipos = i; jpos = j + 1; mpos = 0; /* Allocate storage for the merged elements. */ m = (char*)xmalloc(esize * ((k - i) + 1)); /* Continue while either division has elements to merge. */ while (ipos <= j || jpos <= k) { if (ipos > j) { /* The left division has no more elements to merge. */ while (jpos <= k) { memcpy(&m[mpos * esize], &a[jpos * esize], esize); jpos++; mpos++; } continue; } else if (jpos > k) { /* The right division has no more elements to merge. */ while (ipos <= j) { memcpy(&m[mpos * esize], &a[ipos * esize], esize); ipos++; mpos++; } continue; } /* Append the next ordered element to the merged elements. */ if (comparefn(&a[ipos * esize], &a[jpos * esize]) < 0) { memcpy(&m[mpos * esize], &a[ipos * esize], esize); ipos++; mpos++; } else { memcpy(&m[mpos * esize], &a[jpos * esize], esize); jpos++; mpos++; } } /* Prepare to pass back the merged data. */ memcpy(&a[i * esize], m, esize * ((k - i) + 1)); xfree(m); } void arg_mgsort(void* data, int size, int esize, int i, int k, arg_comparefn* comparefn) { int j; /* Stop the recursion when no more divisions can be made. */ if (i < k) { /* Determine where to divide the elements. */ j = (int)(((i + k - 1)) / 2); /* Recursively sort the two divisions. */ arg_mgsort(data, size, esize, i, j, comparefn); arg_mgsort(data, size, esize, j + 1, k, comparefn); merge(data, esize, i, j, k, comparefn); } } /******************************************************************************* * arg_hashtable: Implements the hash table utilities * * This file is part of the argtable3 library. * * Copyright (C) 2013-2019 Tom G. Huang * * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * * Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * Neither the name of STEWART HEITMANN nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL STEWART HEITMANN BE LIABLE FOR ANY DIRECT, * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ******************************************************************************/ #ifndef ARG_AMALGAMATION #include "argtable3_private.h" #endif #include #include #include #include /* * This hash table module is adapted from the C hash table implementation by * Christopher Clark. Here is the copyright notice from the library: * * Copyright (c) 2002, Christopher Clark * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * * * Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * * * Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * * Neither the name of the original author; nor the names of any contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER * OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ /* * Credit for primes table: Aaron Krowne * http://br.endernet.org/~akrowne/ * http://planetmath.org/encyclopedia/GoodHashTablePrimes.html */ static const unsigned int primes[] = {53, 97, 193, 389, 769, 1543, 3079, 6151, 12289, 24593, 49157, 98317, 196613, 393241, 786433, 1572869, 3145739, 6291469, 12582917, 25165843, 50331653, 100663319, 201326611, 402653189, 805306457, 1610612741}; const unsigned int prime_table_length = sizeof(primes) / sizeof(primes[0]); const float max_load_factor = (float)0.65; static unsigned int enhanced_hash(arg_hashtable_t* h, const void* k) { /* * Aim to protect against poor hash functions by adding logic here. * The logic is taken from Java 1.4 hash table source. */ unsigned int i = h->hashfn(k); i += ~(i << 9); i ^= ((i >> 14) | (i << 18)); /* >>> */ i += (i << 4); i ^= ((i >> 10) | (i << 22)); /* >>> */ return i; } static unsigned int index_for(unsigned int tablelength, unsigned int hashvalue) { return (hashvalue % tablelength); } arg_hashtable_t* arg_hashtable_create(unsigned int minsize, unsigned int (*hashfn)(const void*), int (*eqfn)(const void*, const void*)) { arg_hashtable_t* h; unsigned int pindex; unsigned int size = primes[0]; /* Check requested hash table isn't too large */ if (minsize > (1u << 30)) return NULL; /* * Enforce size as prime. The reason is to avoid clustering of values * into a small number of buckets (yes, distribution). A more even * distributed hash table will perform more consistently. */ for (pindex = 0; pindex < prime_table_length; pindex++) { if (primes[pindex] > minsize) { size = primes[pindex]; break; } } h = (arg_hashtable_t*)xmalloc(sizeof(arg_hashtable_t)); h->table = (struct arg_hashtable_entry**)xmalloc(sizeof(struct arg_hashtable_entry*) * size); memset(h->table, 0, size * sizeof(struct arg_hashtable_entry*)); h->tablelength = size; h->primeindex = pindex; h->entrycount = 0; h->hashfn = hashfn; h->eqfn = eqfn; h->loadlimit = (unsigned int)ceil(size * max_load_factor); return h; } static int arg_hashtable_expand(arg_hashtable_t* h) { /* Double the size of the table to accommodate more entries */ struct arg_hashtable_entry** newtable; struct arg_hashtable_entry* e; unsigned int newsize; unsigned int i; unsigned int index; /* Check we're not hitting max capacity */ if (h->primeindex == (prime_table_length - 1)) return 0; newsize = primes[++(h->primeindex)]; newtable = (struct arg_hashtable_entry**)xmalloc(sizeof(struct arg_hashtable_entry*) * newsize); memset(newtable, 0, newsize * sizeof(struct arg_hashtable_entry*)); /* * This algorithm is not 'stable': it reverses the list * when it transfers entries between the tables */ for (i = 0; i < h->tablelength; i++) { while (NULL != (e = h->table[i])) { h->table[i] = e->next; index = index_for(newsize, e->h); e->next = newtable[index]; newtable[index] = e; } } xfree(h->table); h->table = newtable; h->tablelength = newsize; h->loadlimit = (unsigned int)ceil(newsize * max_load_factor); return -1; } unsigned int arg_hashtable_count(arg_hashtable_t* h) { return h->entrycount; } void arg_hashtable_insert(arg_hashtable_t* h, void* k, void* v) { /* This method allows duplicate keys - but they shouldn't be used */ unsigned int index; struct arg_hashtable_entry* e; if ((h->entrycount + 1) > h->loadlimit) { /* * Ignore the return value. If expand fails, we should * still try cramming just this value into the existing table * -- we may not have memory for a larger table, but one more * element may be ok. Next time we insert, we'll try expanding again. */ arg_hashtable_expand(h); } e = (struct arg_hashtable_entry*)xmalloc(sizeof(struct arg_hashtable_entry)); e->h = enhanced_hash(h, k); index = index_for(h->tablelength, e->h); e->k = k; e->v = v; e->next = h->table[index]; h->table[index] = e; h->entrycount++; } void* arg_hashtable_search(arg_hashtable_t* h, const void* k) { struct arg_hashtable_entry* e; unsigned int hashvalue; unsigned int index; hashvalue = enhanced_hash(h, k); index = index_for(h->tablelength, hashvalue); e = h->table[index]; while (e != NULL) { /* Check hash value to short circuit heavier comparison */ if ((hashvalue == e->h) && (h->eqfn(k, e->k))) return e->v; e = e->next; } return NULL; } void arg_hashtable_remove(arg_hashtable_t* h, const void* k) { /* * TODO: consider compacting the table when the load factor drops enough, * or provide a 'compact' method. */ struct arg_hashtable_entry* e; struct arg_hashtable_entry** pE; unsigned int hashvalue; unsigned int index; hashvalue = enhanced_hash(h, k); index = index_for(h->tablelength, hashvalue); pE = &(h->table[index]); e = *pE; while (NULL != e) { /* Check hash value to short circuit heavier comparison */ if ((hashvalue == e->h) && (h->eqfn(k, e->k))) { *pE = e->next; h->entrycount--; xfree(e->k); xfree(e->v); xfree(e); return; } pE = &(e->next); e = e->next; } } void arg_hashtable_destroy(arg_hashtable_t* h, int free_values) { unsigned int i; struct arg_hashtable_entry *e, *f; struct arg_hashtable_entry** table = h->table; if (free_values) { for (i = 0; i < h->tablelength; i++) { e = table[i]; while (NULL != e) { f = e; e = e->next; xfree(f->k); xfree(f->v); xfree(f); } } } else { for (i = 0; i < h->tablelength; i++) { e = table[i]; while (NULL != e) { f = e; e = e->next; xfree(f->k); xfree(f); } } } xfree(h->table); xfree(h); } arg_hashtable_itr_t* arg_hashtable_itr_create(arg_hashtable_t* h) { unsigned int i; unsigned int tablelength; arg_hashtable_itr_t* itr = (arg_hashtable_itr_t*)xmalloc(sizeof(arg_hashtable_itr_t)); itr->h = h; itr->e = NULL; itr->parent = NULL; tablelength = h->tablelength; itr->index = tablelength; if (0 == h->entrycount) return itr; for (i = 0; i < tablelength; i++) { if (h->table[i] != NULL) { itr->e = h->table[i]; itr->index = i; break; } } return itr; } void arg_hashtable_itr_destroy(arg_hashtable_itr_t* itr) { xfree(itr); } void* arg_hashtable_itr_key(arg_hashtable_itr_t* i) { return i->e->k; } void* arg_hashtable_itr_value(arg_hashtable_itr_t* i) { return i->e->v; } int arg_hashtable_itr_advance(arg_hashtable_itr_t* itr) { unsigned int j; unsigned int tablelength; struct arg_hashtable_entry** table; struct arg_hashtable_entry* next; if (itr->e == NULL) return 0; /* stupidity check */ next = itr->e->next; if (NULL != next) { itr->parent = itr->e; itr->e = next; return -1; } tablelength = itr->h->tablelength; itr->parent = NULL; if (tablelength <= (j = ++(itr->index))) { itr->e = NULL; return 0; } table = itr->h->table; while (NULL == (next = table[j])) { if (++j >= tablelength) { itr->index = tablelength; itr->e = NULL; return 0; } } itr->index = j; itr->e = next; return -1; } int arg_hashtable_itr_remove(arg_hashtable_itr_t* itr) { struct arg_hashtable_entry* remember_e; struct arg_hashtable_entry* remember_parent; int ret; /* Do the removal */ if ((itr->parent) == NULL) { /* element is head of a chain */ itr->h->table[itr->index] = itr->e->next; } else { /* element is mid-chain */ itr->parent->next = itr->e->next; } /* itr->e is now outside the hashtable */ remember_e = itr->e; itr->h->entrycount--; xfree(remember_e->k); xfree(remember_e->v); /* Advance the iterator, correcting the parent */ remember_parent = itr->parent; ret = arg_hashtable_itr_advance(itr); if (itr->parent == remember_e) { itr->parent = remember_parent; } xfree(remember_e); return ret; } int arg_hashtable_itr_search(arg_hashtable_itr_t* itr, arg_hashtable_t* h, void* k) { struct arg_hashtable_entry* e; struct arg_hashtable_entry* parent; unsigned int hashvalue; unsigned int index; hashvalue = enhanced_hash(h, k); index = index_for(h->tablelength, hashvalue); e = h->table[index]; parent = NULL; while (e != NULL) { /* Check hash value to short circuit heavier comparison */ if ((hashvalue == e->h) && (h->eqfn(k, e->k))) { itr->index = index; itr->e = e; itr->parent = parent; itr->h = h; return -1; } parent = e; e = e->next; } return 0; } int arg_hashtable_change(arg_hashtable_t* h, void* k, void* v) { struct arg_hashtable_entry* e; unsigned int hashvalue; unsigned int index; hashvalue = enhanced_hash(h, k); index = index_for(h->tablelength, hashvalue); e = h->table[index]; while (e != NULL) { /* Check hash value to short circuit heavier comparison */ if ((hashvalue == e->h) && (h->eqfn(k, e->k))) { xfree(e->v); e->v = v; return -1; } e = e->next; } return 0; } /******************************************************************************* * arg_dstr: Implements the dynamic string utilities * * This file is part of the argtable3 library. * * Copyright (C) 2013-2019 Tom G. Huang * * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * * Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * Neither the name of STEWART HEITMANN nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL STEWART HEITMANN BE LIABLE FOR ANY DIRECT, * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ******************************************************************************/ #include "argtable3.h" #ifndef ARG_AMALGAMATION #include "argtable3_private.h" #endif #include #include #include #if defined(_MSC_VER) #pragma warning(push) #pragma warning(disable : 4996) #endif #define START_VSNBUFF 16 /* * This dynamic string module is adapted from TclResult.c in the Tcl library. * Here is the copyright notice from the library: * * This software is copyrighted by the Regents of the University of * California, Sun Microsystems, Inc., Scriptics Corporation, ActiveState * Corporation and other parties. The following terms apply to all files * associated with the software unless explicitly disclaimed in * individual files. * * The authors hereby grant permission to use, copy, modify, distribute, * and license this software and its documentation for any purpose, provided * that existing copyright notices are retained in all copies and that this * notice is included verbatim in any distributions. No written agreement, * license, or royalty fee is required for any of the authorized uses. * Modifications to this software may be copyrighted by their authors * and need not follow the licensing terms described here, provided that * the new terms are clearly indicated on the first page of each file where * they apply. * * IN NO EVENT SHALL THE AUTHORS OR DISTRIBUTORS BE LIABLE TO ANY PARTY * FOR DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES * ARISING OUT OF THE USE OF THIS SOFTWARE, ITS DOCUMENTATION, OR ANY * DERIVATIVES THEREOF, EVEN IF THE AUTHORS HAVE BEEN ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. * * THE AUTHORS AND DISTRIBUTORS SPECIFICALLY DISCLAIM ANY WARRANTIES, * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE, AND NON-INFRINGEMENT. THIS SOFTWARE * IS PROVIDED ON AN "AS IS" BASIS, AND THE AUTHORS AND DISTRIBUTORS HAVE * NO OBLIGATION TO PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR * MODIFICATIONS. * * GOVERNMENT USE: If you are acquiring this software on behalf of the * U.S. government, the Government shall have only "Restricted Rights" * in the software and related documentation as defined in the Federal * Acquisition Regulations (FARs) in Clause 52.227.19 (c) (2). If you * are acquiring the software on behalf of the Department of Defense, the * software shall be classified as "Commercial Computer Software" and the * Government shall have only "Restricted Rights" as defined in Clause * 252.227-7014 (b) (3) of DFARs. Notwithstanding the foregoing, the * authors grant the U.S. Government and others acting in its behalf * permission to use and distribute the software in accordance with the * terms specified in this license. */ typedef struct _internal_arg_dstr { char* data; arg_dstr_freefn* free_proc; char sbuf[ARG_DSTR_SIZE + 1]; char* append_data; int append_data_size; int append_used; } _internal_arg_dstr_t; static void setup_append_buf(arg_dstr_t res, int newSpace); arg_dstr_t arg_dstr_create(void) { _internal_arg_dstr_t* h = (_internal_arg_dstr_t*)xmalloc(sizeof(_internal_arg_dstr_t)); memset(h, 0, sizeof(_internal_arg_dstr_t)); h->sbuf[0] = 0; h->data = h->sbuf; h->free_proc = ARG_DSTR_STATIC; return h; } void arg_dstr_destroy(arg_dstr_t ds) { if (ds == NULL) return; arg_dstr_reset(ds); xfree(ds); return; } void arg_dstr_set(arg_dstr_t ds, char* str, arg_dstr_freefn* free_proc) { int length; register arg_dstr_freefn* old_free_proc = ds->free_proc; char* old_result = ds->data; if (str == NULL) { ds->sbuf[0] = 0; ds->data = ds->sbuf; ds->free_proc = ARG_DSTR_STATIC; } else if (free_proc == ARG_DSTR_VOLATILE) { length = (int)strlen(str); if (length > ARG_DSTR_SIZE) { ds->data = (char*)xmalloc((unsigned)length + 1); ds->free_proc = ARG_DSTR_DYNAMIC; } else { ds->data = ds->sbuf; ds->free_proc = ARG_DSTR_STATIC; } strcpy(ds->data, str); } else { ds->data = str; ds->free_proc = free_proc; } /* * If the old result was dynamically-allocated, free it up. Do it here, * rather than at the beginning, in case the new result value was part of * the old result value. */ if ((old_free_proc != 0) && (old_result != ds->data)) { if (old_free_proc == ARG_DSTR_DYNAMIC) { xfree(old_result); } else { (*old_free_proc)(old_result); } } if ((ds->append_data != NULL) && (ds->append_data_size > 0)) { xfree(ds->append_data); ds->append_data = NULL; ds->append_data_size = 0; } } char* arg_dstr_cstr(arg_dstr_t ds) /* Interpreter whose result to return. */ { return ds->data; } void arg_dstr_cat(arg_dstr_t ds, const char* str) { setup_append_buf(ds, (int)strlen(str) + 1); memcpy(ds->data + strlen(ds->data), str, strlen(str)); } void arg_dstr_catc(arg_dstr_t ds, char c) { setup_append_buf(ds, 2); memcpy(ds->data + strlen(ds->data), &c, 1); } /* * The logic of the `arg_dstr_catf` function is adapted from the `bformat` * function in The Better String Library by Paul Hsieh. Here is the copyright * notice from the library: * * Copyright (c) 2014, Paul Hsieh * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * * Redistributions of source code must retain the above copyright notice, this * list of conditions and the following disclaimer. * * * Redistributions in binary form must reproduce the above copyright notice, * this list of conditions and the following disclaimer in the documentation * and/or other materials provided with the distribution. * * * Neither the name of bstrlib nor the names of its * contributors may be used to endorse or promote products derived from * this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ void arg_dstr_catf(arg_dstr_t ds, const char* fmt, ...) { va_list arglist; char* buff; int n, r; size_t slen; if (fmt == NULL) return; /* Since the length is not determinable beforehand, a search is performed using the truncating "vsnprintf" call (to avoid buffer overflows) on increasing potential sizes for the output result. */ if ((n = (int)(2 * strlen(fmt))) < START_VSNBUFF) n = START_VSNBUFF; buff = (char*)xmalloc(n + 2); memset(buff, 0, n + 2); for (;;) { va_start(arglist, fmt); r = vsnprintf(buff, n + 1, fmt, arglist); va_end(arglist); slen = strlen(buff); if (slen < (size_t)n) break; if (r > n) n = r; else n += n; xfree(buff); buff = (char*)xmalloc(n + 2); memset(buff, 0, n + 2); } arg_dstr_cat(ds, buff); xfree(buff); } static void setup_append_buf(arg_dstr_t ds, int new_space) { int total_space; /* * Make the append buffer larger, if that's necessary, then copy the * data into the append buffer and make the append buffer the official * data. */ if (ds->data != ds->append_data) { /* * If the buffer is too big, then free it up so we go back to a * smaller buffer. This avoids tying up memory forever after a large * operation. */ if (ds->append_data_size > 500) { xfree(ds->append_data); ds->append_data = NULL; ds->append_data_size = 0; } ds->append_used = (int)strlen(ds->data); } else if (ds->data[ds->append_used] != 0) { /* * Most likely someone has modified a result created by * arg_dstr_cat et al. so that it has a different size. Just * recompute the size. */ ds->append_used = (int)strlen(ds->data); } total_space = new_space + ds->append_used; if (total_space >= ds->append_data_size) { char* newbuf; if (total_space < 100) { total_space = 200; } else { total_space *= 2; } newbuf = (char*)xmalloc((unsigned)total_space); memset(newbuf, 0, total_space); strcpy(newbuf, ds->data); if (ds->append_data != NULL) { xfree(ds->append_data); } ds->append_data = newbuf; ds->append_data_size = total_space; } else if (ds->data != ds->append_data) { strcpy(ds->append_data, ds->data); } arg_dstr_free(ds); ds->data = ds->append_data; } void arg_dstr_free(arg_dstr_t ds) { if (ds->free_proc != NULL) { if (ds->free_proc == ARG_DSTR_DYNAMIC) { xfree(ds->data); } else { (*ds->free_proc)(ds->data); } ds->free_proc = NULL; } } void arg_dstr_reset(arg_dstr_t ds) { arg_dstr_free(ds); if ((ds->append_data != NULL) && (ds->append_data_size > 0)) { xfree(ds->append_data); ds->append_data = NULL; ds->append_data_size = 0; } ds->data = ds->sbuf; ds->sbuf[0] = 0; } #if defined(_MSC_VER) #pragma warning(pop) #endif /* $NetBSD: getopt.h,v 1.4 2000/07/07 10:43:54 ad Exp $ */ /* $FreeBSD$ */ /*- * SPDX-License-Identifier: BSD-2-Clause-NetBSD * * Copyright (c) 2000 The NetBSD Foundation, Inc. * All rights reserved. * * This code is derived from software contributed to The NetBSD Foundation * by Dieter Baron and Thomas Klausner. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */ #if ARG_REPLACE_GETOPT == 1 #ifndef _GETOPT_H_ #define _GETOPT_H_ /* * GNU-like getopt_long()/getopt_long_only() with 4.4BSD optreset extension. * getopt() is declared here too for GNU programs. */ #define no_argument 0 #define required_argument 1 #define optional_argument 2 struct option { /* name of long option */ const char *name; /* * one of no_argument, required_argument, and optional_argument: * whether option takes an argument */ int has_arg; /* if not NULL, set *flag to val when option found */ int *flag; /* if flag not NULL, value to set *flag to; else return value */ int val; }; #ifdef __cplusplus extern "C" { #endif int getopt_long(int, char * const *, const char *, const struct option *, int *); int getopt_long_only(int, char * const *, const char *, const struct option *, int *); #ifndef _GETOPT_DECLARED #define _GETOPT_DECLARED int getopt(int, char * const [], const char *); extern char *optarg; /* getopt(3) external variables */ extern int optind, opterr, optopt; #endif #ifndef _OPTRESET_DECLARED #define _OPTRESET_DECLARED extern int optreset; /* getopt(3) external variable */ #endif #ifdef __cplusplus } #endif #endif /* !_GETOPT_H_ */ #endif /* ARG_REPLACE_GETOPT == 1 */ /* $OpenBSD: getopt_long.c,v 1.26 2013/06/08 22:47:56 millert Exp $ */ /* $NetBSD: getopt_long.c,v 1.15 2002/01/31 22:43:40 tv Exp $ */ /* * Copyright (c) 2002 Todd C. Miller * * Permission to use, copy, modify, and 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. * * Sponsored in part by the Defense Advanced Research Projects * Agency (DARPA) and Air Force Research Laboratory, Air Force * Materiel Command, USAF, under agreement number F39502-99-1-0512. */ /*- * Copyright (c) 2000 The NetBSD Foundation, Inc. * All rights reserved. * * This code is derived from software contributed to The NetBSD Foundation * by Dieter Baron and Thomas Klausner. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */ #include "argtable3.h" #if ARG_REPLACE_GETOPT == 1 #ifndef ARG_AMALGAMATION #include "arg_getopt.h" #endif #include #include #include #define GNU_COMPATIBLE /* Be more compatible, configure's use us! */ int opterr = 1; /* if error message should be printed */ int optind = 1; /* index into parent argv vector */ int optopt = '?'; /* character checked for validity */ int optreset; /* reset getopt */ char *optarg; /* argument associated with option */ #define PRINT_ERROR ((opterr) && (*options != ':')) #define FLAG_PERMUTE 0x01 /* permute non-options to the end of argv */ #define FLAG_ALLARGS 0x02 /* treat non-options as args to option "-1" */ #define FLAG_LONGONLY 0x04 /* operate as getopt_long_only */ /* return values */ #define BADCH (int)'?' #define BADARG ((*options == ':') ? (int)':' : (int)'?') #define INORDER (int)1 #define EMSG "" #ifdef GNU_COMPATIBLE #define NO_PREFIX (-1) #define D_PREFIX 0 #define DD_PREFIX 1 #define W_PREFIX 2 #endif static int getopt_internal(int, char * const *, const char *, const struct option *, int *, int); static int parse_long_options(char * const *, const char *, const struct option *, int *, int, int); static int gcd(int, int); static void permute_args(int, int, int, char * const *); static char *place = EMSG; /* option letter processing */ /* XXX: set optreset to 1 rather than these two */ static int nonopt_start = -1; /* first non option argument (for permute) */ static int nonopt_end = -1; /* first option after non options (for permute) */ /* Error messages */ static const char recargchar[] = "option requires an argument -- %c"; static const char illoptchar[] = "illegal option -- %c"; /* From P1003.2 */ #ifdef GNU_COMPATIBLE static int dash_prefix = NO_PREFIX; static const char gnuoptchar[] = "invalid option -- %c"; static const char recargstring[] = "option `%s%s' requires an argument"; static const char ambig[] = "option `%s%.*s' is ambiguous"; static const char noarg[] = "option `%s%.*s' doesn't allow an argument"; static const char illoptstring[] = "unrecognized option `%s%s'"; #else static const char recargstring[] = "option requires an argument -- %s"; static const char ambig[] = "ambiguous option -- %.*s"; static const char noarg[] = "option doesn't take an argument -- %.*s"; static const char illoptstring[] = "unknown option -- %s"; #endif #ifdef _WIN32 /* * Windows needs warnx(). We change the definition though: * 1. (another) global is defined, opterrmsg, which holds the error message * 2. errors are always printed out on stderr w/o the program name * Note that opterrmsg always gets set no matter what opterr is set to. The * error message will not be printed if opterr is 0 as usual. */ #include #include #define MAX_OPTERRMSG_SIZE 128 extern char opterrmsg[MAX_OPTERRMSG_SIZE]; char opterrmsg[MAX_OPTERRMSG_SIZE]; /* buffer for the last error message */ static void warnx(const char* fmt, ...) { va_list ap; va_start(ap, fmt); /* * Make sure opterrmsg is always zero-terminated despite the _vsnprintf() * implementation specifics and manually suppress the warning. */ memset(opterrmsg, 0, sizeof(opterrmsg)); if (fmt != NULL) #if (defined(__STDC_LIB_EXT1__) && defined(__STDC_WANT_LIB_EXT1__)) || (defined(__STDC_SECURE_LIB__) && defined(__STDC_WANT_SECURE_LIB__)) _vsnprintf_s(opterrmsg, sizeof(opterrmsg), sizeof(opterrmsg) - 1, fmt, ap); #else _vsnprintf(opterrmsg, sizeof(opterrmsg) - 1, fmt, ap); #endif va_end(ap); #ifdef _MSC_VER #pragma warning(suppress : 6053) #endif fprintf(stderr, "%s\n", opterrmsg); } #else #include #endif /*_WIN32*/ /* * Compute the greatest common divisor of a and b. */ static int gcd(int a, int b) { int c; c = a % b; while (c != 0) { a = b; b = c; c = a % b; } return (b); } /* * Exchange the block from nonopt_start to nonopt_end with the block * from nonopt_end to opt_end (keeping the same order of arguments * in each block). */ static void permute_args(int panonopt_start, int panonopt_end, int opt_end, char * const *nargv) { int cstart, cyclelen, i, j, ncycle, nnonopts, nopts, pos; char *swap; /* * compute lengths of blocks and number and size of cycles */ nnonopts = panonopt_end - panonopt_start; nopts = opt_end - panonopt_end; ncycle = gcd(nnonopts, nopts); cyclelen = (opt_end - panonopt_start) / ncycle; for (i = 0; i < ncycle; i++) { cstart = panonopt_end+i; pos = cstart; for (j = 0; j < cyclelen; j++) { if (pos >= panonopt_end) pos -= nnonopts; else pos += nopts; swap = nargv[pos]; /* LINTED const cast */ ((char **) nargv)[pos] = nargv[cstart]; /* LINTED const cast */ ((char **)nargv)[cstart] = swap; } } } /* * parse_long_options -- * Parse long options in argc/argv argument vector. * Returns -1 if short_too is set and the option does not match long_options. */ static int parse_long_options(char * const *nargv, const char *options, const struct option *long_options, int *idx, int short_too, int flags) { char *current_argv, *has_equal; #ifdef GNU_COMPATIBLE char *current_dash; #endif size_t current_argv_len; int i, match, exact_match, second_partial_match; current_argv = place; #ifdef GNU_COMPATIBLE switch (dash_prefix) { case D_PREFIX: current_dash = "-"; break; case DD_PREFIX: current_dash = "--"; break; case W_PREFIX: current_dash = "-W "; break; default: current_dash = ""; break; } #endif match = -1; exact_match = 0; second_partial_match = 0; optind++; if ((has_equal = strchr(current_argv, '=')) != NULL) { /* argument found (--option=arg) */ current_argv_len = has_equal - current_argv; has_equal++; } else current_argv_len = strlen(current_argv); for (i = 0; long_options[i].name; i++) { /* find matching long option */ if (strncmp(current_argv, long_options[i].name, current_argv_len)) continue; if (strlen(long_options[i].name) == current_argv_len) { /* exact match */ match = i; exact_match = 1; break; } /* * If this is a known short option, don't allow * a partial match of a single character. */ if (short_too && current_argv_len == 1) continue; if (match == -1) /* first partial match */ match = i; else if ((flags & FLAG_LONGONLY) || long_options[i].has_arg != long_options[match].has_arg || long_options[i].flag != long_options[match].flag || long_options[i].val != long_options[match].val) second_partial_match = 1; } if (!exact_match && second_partial_match) { /* ambiguous abbreviation */ if (PRINT_ERROR) warnx(ambig, #ifdef GNU_COMPATIBLE current_dash, #endif (int)current_argv_len, current_argv); optopt = 0; return (BADCH); } if (match != -1) { /* option found */ if (long_options[match].has_arg == no_argument && has_equal) { if (PRINT_ERROR) warnx(noarg, #ifdef GNU_COMPATIBLE current_dash, #endif (int)current_argv_len, current_argv); /* * XXX: GNU sets optopt to val regardless of flag */ if (long_options[match].flag == NULL) optopt = long_options[match].val; else optopt = 0; #ifdef GNU_COMPATIBLE return (BADCH); #else return (BADARG); #endif } if (long_options[match].has_arg == required_argument || long_options[match].has_arg == optional_argument) { if (has_equal) optarg = has_equal; else if (long_options[match].has_arg == required_argument) { /* * optional argument doesn't use next nargv */ optarg = nargv[optind++]; } } if ((long_options[match].has_arg == required_argument) && (optarg == NULL)) { /* * Missing argument; leading ':' indicates no error * should be generated. */ if (PRINT_ERROR) warnx(recargstring, #ifdef GNU_COMPATIBLE current_dash, #endif current_argv); /* * XXX: GNU sets optopt to val regardless of flag */ if (long_options[match].flag == NULL) optopt = long_options[match].val; else optopt = 0; --optind; return (BADARG); } } else { /* unknown option */ if (short_too) { --optind; return (-1); } if (PRINT_ERROR) warnx(illoptstring, #ifdef GNU_COMPATIBLE current_dash, #endif current_argv); optopt = 0; return (BADCH); } if (idx) *idx = match; if (long_options[match].flag) { *long_options[match].flag = long_options[match].val; return (0); } else return (long_options[match].val); } /* * getopt_internal -- * Parse argc/argv argument vector. Called by user level routines. */ static int getopt_internal(int nargc, char * const *nargv, const char *options, const struct option *long_options, int *idx, int flags) { char *oli; /* option letter list index */ int optchar, short_too; static int posixly_correct = -1; if (options == NULL) return (-1); /* * XXX Some GNU programs (like cvs) set optind to 0 instead of * XXX using optreset. Work around this braindamage. */ if (optind == 0) optind = optreset = 1; /* * Disable GNU extensions if POSIXLY_CORRECT is set or options * string begins with a '+'. */ if (posixly_correct == -1 || optreset) { #if defined(_WIN32) && ((defined(__STDC_LIB_EXT1__) && defined(__STDC_WANT_LIB_EXT1__)) || (defined(__STDC_SECURE_LIB__) && defined(__STDC_WANT_SECURE_LIB__))) size_t requiredSize; getenv_s(&requiredSize, NULL, 0, "POSIXLY_CORRECT"); posixly_correct = requiredSize != 0; #else posixly_correct = (getenv("POSIXLY_CORRECT") != NULL); #endif } if (*options == '-') flags |= FLAG_ALLARGS; else if (posixly_correct || *options == '+') flags &= ~FLAG_PERMUTE; if (*options == '+' || *options == '-') options++; optarg = NULL; if (optreset) nonopt_start = nonopt_end = -1; start: if (optreset || !*place) { /* update scanning pointer */ optreset = 0; if (optind >= nargc) { /* end of argument vector */ place = EMSG; if (nonopt_end != -1) { /* do permutation, if we have to */ permute_args(nonopt_start, nonopt_end, optind, nargv); optind -= nonopt_end - nonopt_start; } else if (nonopt_start != -1) { /* * If we skipped non-options, set optind * to the first of them. */ optind = nonopt_start; } nonopt_start = nonopt_end = -1; return (-1); } if (*(place = nargv[optind]) != '-' || #ifdef GNU_COMPATIBLE place[1] == '\0') { #else (place[1] == '\0' && strchr(options, '-') == NULL)) { #endif place = EMSG; /* found non-option */ if (flags & FLAG_ALLARGS) { /* * GNU extension: * return non-option as argument to option 1 */ optarg = nargv[optind++]; return (INORDER); } if (!(flags & FLAG_PERMUTE)) { /* * If no permutation wanted, stop parsing * at first non-option. */ return (-1); } /* do permutation */ if (nonopt_start == -1) nonopt_start = optind; else if (nonopt_end != -1) { permute_args(nonopt_start, nonopt_end, optind, nargv); nonopt_start = optind - (nonopt_end - nonopt_start); nonopt_end = -1; } optind++; /* process next argument */ goto start; } if (nonopt_start != -1 && nonopt_end == -1) nonopt_end = optind; /* * If we have "-" do nothing, if "--" we are done. */ if (place[1] != '\0' && *++place == '-' && place[1] == '\0') { optind++; place = EMSG; /* * We found an option (--), so if we skipped * non-options, we have to permute. */ if (nonopt_end != -1) { permute_args(nonopt_start, nonopt_end, optind, nargv); optind -= nonopt_end - nonopt_start; } nonopt_start = nonopt_end = -1; return (-1); } } /* * Check long options if: * 1) we were passed some * 2) the arg is not just "-" * 3) either the arg starts with -- we are getopt_long_only() */ if (long_options != NULL && place != nargv[optind] && (*place == '-' || (flags & FLAG_LONGONLY))) { short_too = 0; #ifdef GNU_COMPATIBLE dash_prefix = D_PREFIX; #endif if (*place == '-') { place++; /* --foo long option */ if (*place == '\0') return (BADARG); /* malformed option */ #ifdef GNU_COMPATIBLE dash_prefix = DD_PREFIX; #endif } else if (*place != ':' && strchr(options, *place) != NULL) short_too = 1; /* could be short option too */ optchar = parse_long_options(nargv, options, long_options, idx, short_too, flags); if (optchar != -1) { place = EMSG; return (optchar); } } if ((optchar = (int)*place++) == (int)':' || (optchar == (int)'-' && *place != '\0') || (oli = strchr(options, optchar)) == NULL) { /* * If the user specified "-" and '-' isn't listed in * options, return -1 (non-option) as per POSIX. * Otherwise, it is an unknown option character (or ':'). */ if (optchar == (int)'-' && *place == '\0') return (-1); if (!*place) ++optind; #ifdef GNU_COMPATIBLE if (PRINT_ERROR) warnx(posixly_correct ? illoptchar : gnuoptchar, optchar); #else if (PRINT_ERROR) warnx(illoptchar, optchar); #endif optopt = optchar; return (BADCH); } if (long_options != NULL && optchar == 'W' && oli[1] == ';') { /* -W long-option */ if (*place) /* no space */ /* NOTHING */; else if (++optind >= nargc) { /* no arg */ place = EMSG; if (PRINT_ERROR) warnx(recargchar, optchar); optopt = optchar; return (BADARG); } else /* white space */ place = nargv[optind]; #ifdef GNU_COMPATIBLE dash_prefix = W_PREFIX; #endif optchar = parse_long_options(nargv, options, long_options, idx, 0, flags); place = EMSG; return (optchar); } if (*++oli != ':') { /* doesn't take argument */ if (!*place) ++optind; } else { /* takes (optional) argument */ optarg = NULL; if (*place) /* no white space */ optarg = place; else if (oli[1] != ':') { /* arg not optional */ if (++optind >= nargc) { /* no arg */ place = EMSG; if (PRINT_ERROR) warnx(recargchar, optchar); optopt = optchar; return (BADARG); } else optarg = nargv[optind]; } place = EMSG; ++optind; } /* dump back option letter */ return (optchar); } /* * getopt -- * Parse argc/argv argument vector. * * [eventually this will replace the BSD getopt] */ int getopt(int nargc, char * const *nargv, const char *options) { /* * We don't pass FLAG_PERMUTE to getopt_internal() since * the BSD getopt(3) (unlike GNU) has never done this. * * Furthermore, since many privileged programs call getopt() * before dropping privileges it makes sense to keep things * as simple (and bug-free) as possible. */ return (getopt_internal(nargc, nargv, options, NULL, NULL, 0)); } /* * getopt_long -- * Parse argc/argv argument vector. */ int getopt_long(int nargc, char * const *nargv, const char *options, const struct option *long_options, int *idx) { return (getopt_internal(nargc, nargv, options, long_options, idx, FLAG_PERMUTE)); } /* * getopt_long_only -- * Parse argc/argv argument vector. */ int getopt_long_only(int nargc, char * const *nargv, const char *options, const struct option *long_options, int *idx) { return (getopt_internal(nargc, nargv, options, long_options, idx, FLAG_PERMUTE|FLAG_LONGONLY)); } #endif /* ARG_REPLACE_GETOPT == 1 */ /******************************************************************************* * arg_date: Implements the date command-line option * * This file is part of the argtable3 library. * * Copyright (C) 1998-2001,2003-2011,2013 Stewart Heitmann * * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * * Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * Neither the name of STEWART HEITMANN nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL STEWART HEITMANN BE LIABLE FOR ANY DIRECT, * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ******************************************************************************/ #include "argtable3.h" #ifndef ARG_AMALGAMATION #include "argtable3_private.h" #endif #include #include char* arg_strptime(const char* buf, const char* fmt, struct tm* tm); static void arg_date_resetfn(struct arg_date* parent) { ARG_TRACE(("%s:resetfn(%p)\n", __FILE__, parent)); parent->count = 0; } static int arg_date_scanfn(struct arg_date* parent, const char* argval) { int errorcode = 0; if (parent->count == parent->hdr.maxcount) { errorcode = ARG_ERR_MAXCOUNT; } else if (!argval) { /* no argument value was given, leave parent->tmval[] unaltered but still count it */ parent->count++; } else { const char* pend; struct tm tm = parent->tmval[parent->count]; /* parse the given argument value, store result in parent->tmval[] */ pend = arg_strptime(argval, parent->format, &tm); if (pend && pend[0] == '\0') parent->tmval[parent->count++] = tm; else errorcode = ARG_ERR_BADDATE; } ARG_TRACE(("%s:scanfn(%p) returns %d\n", __FILE__, parent, errorcode)); return errorcode; } static int arg_date_checkfn(struct arg_date* parent) { int errorcode = (parent->count < parent->hdr.mincount) ? ARG_ERR_MINCOUNT : 0; ARG_TRACE(("%s:checkfn(%p) returns %d\n", __FILE__, parent, errorcode)); return errorcode; } static void arg_date_errorfn(struct arg_date* parent, arg_dstr_t ds, int errorcode, const char* argval, const char* progname) { const char* shortopts = parent->hdr.shortopts; const char* longopts = parent->hdr.longopts; const char* datatype = parent->hdr.datatype; /* make argval NULL safe */ argval = argval ? argval : ""; arg_dstr_catf(ds, "%s: ", progname); switch (errorcode) { case ARG_ERR_MINCOUNT: arg_dstr_cat(ds, "missing option "); arg_print_option_ds(ds, shortopts, longopts, datatype, "\n"); break; case ARG_ERR_MAXCOUNT: arg_dstr_cat(ds, "excess option "); arg_print_option_ds(ds, shortopts, longopts, argval, "\n"); break; case ARG_ERR_BADDATE: { struct tm tm; char buff[200]; arg_dstr_catf(ds, "illegal timestamp format \"%s\"\n", argval); memset(&tm, 0, sizeof(tm)); arg_strptime("1999-12-31 23:59:59", "%F %H:%M:%S", &tm); strftime(buff, sizeof(buff), parent->format, &tm); arg_dstr_catf(ds, "correct format is \"%s\"\n", buff); break; } } } struct arg_date* arg_date0(const char* shortopts, const char* longopts, const char* format, const char* datatype, const char* glossary) { return arg_daten(shortopts, longopts, format, datatype, 0, 1, glossary); } struct arg_date* arg_date1(const char* shortopts, const char* longopts, const char* format, const char* datatype, const char* glossary) { return arg_daten(shortopts, longopts, format, datatype, 1, 1, glossary); } struct arg_date* arg_daten(const char* shortopts, const char* longopts, const char* format, const char* datatype, int mincount, int maxcount, const char* glossary) { size_t nbytes; struct arg_date* result; /* foolproof things by ensuring maxcount is not less than mincount */ maxcount = (maxcount < mincount) ? mincount : maxcount; /* default time format is the national date format for the locale */ if (!format) format = "%x"; nbytes = sizeof(struct arg_date) /* storage for struct arg_date */ + maxcount * sizeof(struct tm); /* storage for tmval[maxcount] array */ /* allocate storage for the arg_date struct + tmval[] array. */ /* we use calloc because we want the tmval[] array zero filled. */ result = (struct arg_date*)xcalloc(1, nbytes); /* init the arg_hdr struct */ result->hdr.flag = ARG_HASVALUE; result->hdr.shortopts = shortopts; result->hdr.longopts = longopts; result->hdr.datatype = datatype ? datatype : format; result->hdr.glossary = glossary; result->hdr.mincount = mincount; result->hdr.maxcount = maxcount; result->hdr.parent = result; result->hdr.resetfn = (arg_resetfn*)arg_date_resetfn; result->hdr.scanfn = (arg_scanfn*)arg_date_scanfn; result->hdr.checkfn = (arg_checkfn*)arg_date_checkfn; result->hdr.errorfn = (arg_errorfn*)arg_date_errorfn; /* store the tmval[maxcount] array immediately after the arg_date struct */ result->tmval = (struct tm*)(result + 1); /* init the remaining arg_date member variables */ result->count = 0; result->format = format; ARG_TRACE(("arg_daten() returns %p\n", result)); return result; } /*- * Copyright (c) 1997, 1998, 2005, 2008 The NetBSD Foundation, Inc. * All rights reserved. * * This code was contributed to The NetBSD Foundation by Klaus Klein. * Heavily optimised by David Laight * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */ #include #include #include /* * We do not implement alternate representations. However, we always * check whether a given modifier is allowed for a certain conversion. */ #define ALT_E 0x01 #define ALT_O 0x02 #define LEGAL_ALT(x) \ { \ if (alt_format & ~(x)) \ return (0); \ } #define TM_YEAR_BASE (1900) static int conv_num(const char**, int*, int, int); static const char* day[7] = {"Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday"}; static const char* abday[7] = {"Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"}; static const char* mon[12] = {"January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"}; static const char* abmon[12] = {"Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"}; static const char* am_pm[2] = {"AM", "PM"}; static int arg_strcasecmp(const char* s1, const char* s2) { const unsigned char* us1 = (const unsigned char*)s1; const unsigned char* us2 = (const unsigned char*)s2; while (tolower(*us1) == tolower(*us2++)) if (*us1++ == '\0') return 0; return tolower(*us1) - tolower(*--us2); } static int arg_strncasecmp(const char* s1, const char* s2, size_t n) { if (n != 0) { const unsigned char* us1 = (const unsigned char*)s1; const unsigned char* us2 = (const unsigned char*)s2; do { if (tolower(*us1) != tolower(*us2++)) return tolower(*us1) - tolower(*--us2); if (*us1++ == '\0') break; } while (--n != 0); } return 0; } char* arg_strptime(const char* buf, const char* fmt, struct tm* tm) { char c; const char* bp; size_t len = 0; int alt_format, i, split_year = 0; bp = buf; while ((c = *fmt) != '\0') { /* Clear `alternate' modifier prior to new conversion. */ alt_format = 0; /* Eat up white-space. */ if (isspace(c)) { while (isspace((int)(*bp))) bp++; fmt++; continue; } if ((c = *fmt++) != '%') goto literal; again: switch (c = *fmt++) { case '%': /* "%%" is converted to "%". */ literal: if (c != *bp++) return (0); break; /* * "Alternative" modifiers. Just set the appropriate flag * and start over again. */ case 'E': /* "%E?" alternative conversion modifier. */ LEGAL_ALT(0); alt_format |= ALT_E; goto again; case 'O': /* "%O?" alternative conversion modifier. */ LEGAL_ALT(0); alt_format |= ALT_O; goto again; /* * "Complex" conversion rules, implemented through recursion. */ case 'c': /* Date and time, using the locale's format. */ LEGAL_ALT(ALT_E); bp = arg_strptime(bp, "%x %X", tm); if (!bp) return (0); break; case 'D': /* The date as "%m/%d/%y". */ LEGAL_ALT(0); bp = arg_strptime(bp, "%m/%d/%y", tm); if (!bp) return (0); break; case 'R': /* The time as "%H:%M". */ LEGAL_ALT(0); bp = arg_strptime(bp, "%H:%M", tm); if (!bp) return (0); break; case 'r': /* The time in 12-hour clock representation. */ LEGAL_ALT(0); bp = arg_strptime(bp, "%I:%M:%S %p", tm); if (!bp) return (0); break; case 'T': /* The time as "%H:%M:%S". */ LEGAL_ALT(0); bp = arg_strptime(bp, "%H:%M:%S", tm); if (!bp) return (0); break; case 'X': /* The time, using the locale's format. */ LEGAL_ALT(ALT_E); bp = arg_strptime(bp, "%H:%M:%S", tm); if (!bp) return (0); break; case 'x': /* The date, using the locale's format. */ LEGAL_ALT(ALT_E); bp = arg_strptime(bp, "%m/%d/%y", tm); if (!bp) return (0); break; /* * "Elementary" conversion rules. */ case 'A': /* The day of week, using the locale's form. */ case 'a': LEGAL_ALT(0); for (i = 0; i < 7; i++) { /* Full name. */ len = strlen(day[i]); if (arg_strncasecmp(day[i], bp, len) == 0) break; /* Abbreviated name. */ len = strlen(abday[i]); if (arg_strncasecmp(abday[i], bp, len) == 0) break; } /* Nothing matched. */ if (i == 7) return (0); tm->tm_wday = i; bp += len; break; case 'B': /* The month, using the locale's form. */ case 'b': case 'h': LEGAL_ALT(0); for (i = 0; i < 12; i++) { /* Full name. */ len = strlen(mon[i]); if (arg_strncasecmp(mon[i], bp, len) == 0) break; /* Abbreviated name. */ len = strlen(abmon[i]); if (arg_strncasecmp(abmon[i], bp, len) == 0) break; } /* Nothing matched. */ if (i == 12) return (0); tm->tm_mon = i; bp += len; break; case 'C': /* The century number. */ LEGAL_ALT(ALT_E); if (!(conv_num(&bp, &i, 0, 99))) return (0); if (split_year) { tm->tm_year = (tm->tm_year % 100) + (i * 100); } else { tm->tm_year = i * 100; split_year = 1; } break; case 'd': /* The day of month. */ case 'e': LEGAL_ALT(ALT_O); if (!(conv_num(&bp, &tm->tm_mday, 1, 31))) return (0); break; case 'k': /* The hour (24-hour clock representation). */ LEGAL_ALT(0); /* FALLTHROUGH */ case 'H': LEGAL_ALT(ALT_O); if (!(conv_num(&bp, &tm->tm_hour, 0, 23))) return (0); break; case 'l': /* The hour (12-hour clock representation). */ LEGAL_ALT(0); /* FALLTHROUGH */ case 'I': LEGAL_ALT(ALT_O); if (!(conv_num(&bp, &tm->tm_hour, 1, 12))) return (0); if (tm->tm_hour == 12) tm->tm_hour = 0; break; case 'j': /* The day of year. */ LEGAL_ALT(0); if (!(conv_num(&bp, &i, 1, 366))) return (0); tm->tm_yday = i - 1; break; case 'M': /* The minute. */ LEGAL_ALT(ALT_O); if (!(conv_num(&bp, &tm->tm_min, 0, 59))) return (0); break; case 'm': /* The month. */ LEGAL_ALT(ALT_O); if (!(conv_num(&bp, &i, 1, 12))) return (0); tm->tm_mon = i - 1; break; case 'p': /* The locale's equivalent of AM/PM. */ LEGAL_ALT(0); /* AM? */ if (arg_strcasecmp(am_pm[0], bp) == 0) { if (tm->tm_hour > 11) return (0); bp += strlen(am_pm[0]); break; } /* PM? */ else if (arg_strcasecmp(am_pm[1], bp) == 0) { if (tm->tm_hour > 11) return (0); tm->tm_hour += 12; bp += strlen(am_pm[1]); break; } /* Nothing matched. */ return (0); case 'S': /* The seconds. */ LEGAL_ALT(ALT_O); if (!(conv_num(&bp, &tm->tm_sec, 0, 61))) return (0); break; case 'U': /* The week of year, beginning on sunday. */ case 'W': /* The week of year, beginning on monday. */ LEGAL_ALT(ALT_O); /* * XXX This is bogus, as we can not assume any valid * information present in the tm structure at this * point to calculate a real value, so just check the * range for now. */ if (!(conv_num(&bp, &i, 0, 53))) return (0); break; case 'w': /* The day of week, beginning on sunday. */ LEGAL_ALT(ALT_O); if (!(conv_num(&bp, &tm->tm_wday, 0, 6))) return (0); break; case 'Y': /* The year. */ LEGAL_ALT(ALT_E); if (!(conv_num(&bp, &i, 0, 9999))) return (0); tm->tm_year = i - TM_YEAR_BASE; break; case 'y': /* The year within 100 years of the epoch. */ LEGAL_ALT(ALT_E | ALT_O); if (!(conv_num(&bp, &i, 0, 99))) return (0); if (split_year) { tm->tm_year = ((tm->tm_year / 100) * 100) + i; break; } split_year = 1; if (i <= 68) tm->tm_year = i + 2000 - TM_YEAR_BASE; else tm->tm_year = i + 1900 - TM_YEAR_BASE; break; /* * Miscellaneous conversions. */ case 'n': /* Any kind of white-space. */ case 't': LEGAL_ALT(0); while (isspace((int)(*bp))) bp++; break; default: /* Unknown/unsupported conversion. */ return (0); } } /* LINTED functional specification */ return ((char*)bp); } static int conv_num(const char** buf, int* dest, int llim, int ulim) { int result = 0; /* The limit also determines the number of valid digits. */ int rulim = ulim; if (**buf < '0' || **buf > '9') return (0); do { result *= 10; result += *(*buf)++ - '0'; rulim /= 10; } while ((result * 10 <= ulim) && rulim && **buf >= '0' && **buf <= '9'); if (result < llim || result > ulim) return (0); *dest = result; return (1); } /******************************************************************************* * arg_dbl: Implements the double command-line option * * This file is part of the argtable3 library. * * Copyright (C) 1998-2001,2003-2011,2013 Stewart Heitmann * * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * * Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * Neither the name of STEWART HEITMANN nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL STEWART HEITMANN BE LIABLE FOR ANY DIRECT, * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ******************************************************************************/ #include "argtable3.h" #ifndef ARG_AMALGAMATION #include "argtable3_private.h" #endif #include static void arg_dbl_resetfn(struct arg_dbl* parent) { ARG_TRACE(("%s:resetfn(%p)\n", __FILE__, parent)); parent->count = 0; } static int arg_dbl_scanfn(struct arg_dbl* parent, const char* argval) { int errorcode = 0; if (parent->count == parent->hdr.maxcount) { /* maximum number of arguments exceeded */ errorcode = ARG_ERR_MAXCOUNT; } else if (!argval) { /* a valid argument with no argument value was given. */ /* This happens when an optional argument value was invoked. */ /* leave parent argument value unaltered but still count the argument. */ parent->count++; } else { double val; char* end; /* extract double from argval into val */ val = strtod(argval, &end); /* if success then store result in parent->dval[] array otherwise return error*/ if (*end == 0) parent->dval[parent->count++] = val; else errorcode = ARG_ERR_BADDOUBLE; } ARG_TRACE(("%s:scanfn(%p) returns %d\n", __FILE__, parent, errorcode)); return errorcode; } static int arg_dbl_checkfn(struct arg_dbl* parent) { int errorcode = (parent->count < parent->hdr.mincount) ? ARG_ERR_MINCOUNT : 0; ARG_TRACE(("%s:checkfn(%p) returns %d\n", __FILE__, parent, errorcode)); return errorcode; } static void arg_dbl_errorfn(struct arg_dbl* parent, arg_dstr_t ds, int errorcode, const char* argval, const char* progname) { const char* shortopts = parent->hdr.shortopts; const char* longopts = parent->hdr.longopts; const char* datatype = parent->hdr.datatype; /* make argval NULL safe */ argval = argval ? argval : ""; arg_dstr_catf(ds, "%s: ", progname); switch (errorcode) { case ARG_ERR_MINCOUNT: arg_dstr_cat(ds, "missing option "); arg_print_option_ds(ds, shortopts, longopts, datatype, "\n"); break; case ARG_ERR_MAXCOUNT: arg_dstr_cat(ds, "excess option "); arg_print_option_ds(ds, shortopts, longopts, argval, "\n"); break; case ARG_ERR_BADDOUBLE: arg_dstr_catf(ds, "invalid argument \"%s\" to option ", argval); arg_print_option_ds(ds, shortopts, longopts, datatype, "\n"); break; } } struct arg_dbl* arg_dbl0(const char* shortopts, const char* longopts, const char* datatype, const char* glossary) { return arg_dbln(shortopts, longopts, datatype, 0, 1, glossary); } struct arg_dbl* arg_dbl1(const char* shortopts, const char* longopts, const char* datatype, const char* glossary) { return arg_dbln(shortopts, longopts, datatype, 1, 1, glossary); } struct arg_dbl* arg_dbln(const char* shortopts, const char* longopts, const char* datatype, int mincount, int maxcount, const char* glossary) { size_t nbytes; struct arg_dbl* result; size_t addr; size_t rem; /* foolproof things by ensuring maxcount is not less than mincount */ maxcount = (maxcount < mincount) ? mincount : maxcount; nbytes = sizeof(struct arg_dbl) /* storage for struct arg_dbl */ + (maxcount + 1) * sizeof(double); /* storage for dval[maxcount] array plus one extra for padding to memory boundary */ result = (struct arg_dbl*)xmalloc(nbytes); /* init the arg_hdr struct */ result->hdr.flag = ARG_HASVALUE; result->hdr.shortopts = shortopts; result->hdr.longopts = longopts; result->hdr.datatype = datatype ? datatype : ""; result->hdr.glossary = glossary; result->hdr.mincount = mincount; result->hdr.maxcount = maxcount; result->hdr.parent = result; result->hdr.resetfn = (arg_resetfn*)arg_dbl_resetfn; result->hdr.scanfn = (arg_scanfn*)arg_dbl_scanfn; result->hdr.checkfn = (arg_checkfn*)arg_dbl_checkfn; result->hdr.errorfn = (arg_errorfn*)arg_dbl_errorfn; /* Store the dval[maxcount] array on the first double boundary that * immediately follows the arg_dbl struct. We do the memory alignment * purely for SPARC and Motorola systems. They require floats and * doubles to be aligned on natural boundaries. */ addr = (size_t)(result + 1); rem = addr % sizeof(double); result->dval = (double*)(addr + sizeof(double) - rem); ARG_TRACE(("addr=%p, dval=%p, sizeof(double)=%d rem=%d\n", addr, result->dval, (int)sizeof(double), (int)rem)); result->count = 0; ARG_TRACE(("arg_dbln() returns %p\n", result)); return result; } /******************************************************************************* * arg_end: Implements the error handling utilities * * This file is part of the argtable3 library. * * Copyright (C) 1998-2001,2003-2011,2013 Stewart Heitmann * * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * * Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * Neither the name of STEWART HEITMANN nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL STEWART HEITMANN BE LIABLE FOR ANY DIRECT, * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ******************************************************************************/ #include "argtable3.h" #ifndef ARG_AMALGAMATION #include "argtable3_private.h" #endif #include static void arg_end_resetfn(struct arg_end* parent) { ARG_TRACE(("%s:resetfn(%p)\n", __FILE__, parent)); parent->count = 0; } static void arg_end_errorfn(void* parent, arg_dstr_t ds, int error, const char* argval, const char* progname) { /* suppress unreferenced formal parameter warning */ (void)parent; progname = progname ? progname : ""; argval = argval ? argval : ""; arg_dstr_catf(ds, "%s: ", progname); switch (error) { case ARG_ELIMIT: arg_dstr_cat(ds, "too many errors to display"); break; case ARG_EMALLOC: arg_dstr_cat(ds, "insufficient memory"); break; case ARG_ENOMATCH: arg_dstr_catf(ds, "unexpected argument \"%s\"", argval); break; case ARG_EMISSARG: arg_dstr_catf(ds, "option \"%s\" requires an argument", argval); break; case ARG_ELONGOPT: arg_dstr_catf(ds, "invalid option \"%s\"", argval); break; default: arg_dstr_catf(ds, "invalid option \"-%c\"", error); break; } arg_dstr_cat(ds, "\n"); } struct arg_end* arg_end(int maxcount) { size_t nbytes; struct arg_end* result; nbytes = sizeof(struct arg_end) + maxcount * sizeof(int) /* storage for int error[maxcount] array*/ + maxcount * sizeof(void*) /* storage for void* parent[maxcount] array */ + maxcount * sizeof(char*); /* storage for char* argval[maxcount] array */ result = (struct arg_end*)xmalloc(nbytes); /* init the arg_hdr struct */ result->hdr.flag = ARG_TERMINATOR; result->hdr.shortopts = NULL; result->hdr.longopts = NULL; result->hdr.datatype = NULL; result->hdr.glossary = NULL; result->hdr.mincount = 1; result->hdr.maxcount = maxcount; result->hdr.parent = result; result->hdr.resetfn = (arg_resetfn*)arg_end_resetfn; result->hdr.scanfn = NULL; result->hdr.checkfn = NULL; result->hdr.errorfn = (arg_errorfn*)arg_end_errorfn; /* store error[maxcount] array immediately after struct arg_end */ result->error = (int*)(result + 1); /* store parent[maxcount] array immediately after error[] array */ result->parent = (void**)(result->error + maxcount); /* store argval[maxcount] array immediately after parent[] array */ result->argval = (const char**)(result->parent + maxcount); ARG_TRACE(("arg_end(%d) returns %p\n", maxcount, result)); return result; } void arg_print_errors_ds(arg_dstr_t ds, struct arg_end* end, const char* progname) { int i; ARG_TRACE(("arg_errors()\n")); for (i = 0; i < end->count; i++) { struct arg_hdr* errorparent = (struct arg_hdr*)(end->parent[i]); if (errorparent->errorfn) errorparent->errorfn(end->parent[i], ds, end->error[i], end->argval[i], progname); } } void arg_print_errors(FILE* fp, struct arg_end* end, const char* progname) { arg_dstr_t ds = arg_dstr_create(); arg_print_errors_ds(ds, end, progname); fputs(arg_dstr_cstr(ds), fp); arg_dstr_destroy(ds); } /******************************************************************************* * arg_file: Implements the file command-line option * * This file is part of the argtable3 library. * * Copyright (C) 1998-2001,2003-2011,2013 Stewart Heitmann * * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * * Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * Neither the name of STEWART HEITMANN nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL STEWART HEITMANN BE LIABLE FOR ANY DIRECT, * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ******************************************************************************/ #include "argtable3.h" #ifndef ARG_AMALGAMATION #include "argtable3_private.h" #endif #include #include #ifdef WIN32 #define FILESEPARATOR1 '\\' #define FILESEPARATOR2 '/' #else #define FILESEPARATOR1 '/' #define FILESEPARATOR2 '/' #endif static void arg_file_resetfn(struct arg_file* parent) { ARG_TRACE(("%s:resetfn(%p)\n", __FILE__, parent)); parent->count = 0; } /* Returns ptr to the base filename within *filename */ static const char* arg_basename(const char* filename) { const char *result = NULL, *result1, *result2; /* Find the last occurrence of other file separator character. */ /* Two alternative file separator chars are supported as legal */ /* file separators but not both together in the same filename. */ result1 = (filename ? strrchr(filename, FILESEPARATOR1) : NULL); result2 = (filename ? strrchr(filename, FILESEPARATOR2) : NULL); if (result2) result = result2 + 1; /* using FILESEPARATOR2 (the alternative file separator) */ if (result1) result = result1 + 1; /* using FILESEPARATOR1 (the preferred file separator) */ if (!result) result = filename; /* neither file separator was found so basename is the whole filename */ /* special cases of "." and ".." are not considered basenames */ if (result && (strcmp(".", result) == 0 || strcmp("..", result) == 0)) result = filename + strlen(filename); return result; } /* Returns ptr to the file extension within *basename */ static const char* arg_extension(const char* basename) { /* find the last occurrence of '.' in basename */ const char* result = (basename ? strrchr(basename, '.') : NULL); /* if no '.' was found then return pointer to end of basename */ if (basename && !result) result = basename + strlen(basename); /* special case: basenames with a single leading dot (eg ".foo") are not considered as true extensions */ if (basename && result == basename) result = basename + strlen(basename); /* special case: empty extensions (eg "foo.","foo..") are not considered as true extensions */ if (basename && result && strlen(result) == 1) result = basename + strlen(basename); return result; } static int arg_file_scanfn(struct arg_file* parent, const char* argval) { int errorcode = 0; if (parent->count == parent->hdr.maxcount) { /* maximum number of arguments exceeded */ errorcode = ARG_ERR_MAXCOUNT; } else if (!argval) { /* a valid argument with no argument value was given. */ /* This happens when an optional argument value was invoked. */ /* leave parent argument value unaltered but still count the argument. */ parent->count++; } else { parent->filename[parent->count] = argval; parent->basename[parent->count] = arg_basename(argval); parent->extension[parent->count] = arg_extension(parent->basename[parent->count]); /* only seek extensions within the basename (not the file path)*/ parent->count++; } ARG_TRACE(("%s4:scanfn(%p) returns %d\n", __FILE__, parent, errorcode)); return errorcode; } static int arg_file_checkfn(struct arg_file* parent) { int errorcode = (parent->count < parent->hdr.mincount) ? ARG_ERR_MINCOUNT : 0; ARG_TRACE(("%s:checkfn(%p) returns %d\n", __FILE__, parent, errorcode)); return errorcode; } static void arg_file_errorfn(struct arg_file* parent, arg_dstr_t ds, int errorcode, const char* argval, const char* progname) { const char* shortopts = parent->hdr.shortopts; const char* longopts = parent->hdr.longopts; const char* datatype = parent->hdr.datatype; /* make argval NULL safe */ argval = argval ? argval : ""; arg_dstr_catf(ds, "%s: ", progname); switch (errorcode) { case ARG_ERR_MINCOUNT: arg_dstr_cat(ds, "missing option "); arg_print_option_ds(ds, shortopts, longopts, datatype, "\n"); break; case ARG_ERR_MAXCOUNT: arg_dstr_cat(ds, "excess option "); arg_print_option_ds(ds, shortopts, longopts, argval, "\n"); break; default: arg_dstr_catf(ds, "unknown error at \"%s\"\n", argval); } } struct arg_file* arg_file0(const char* shortopts, const char* longopts, const char* datatype, const char* glossary) { return arg_filen(shortopts, longopts, datatype, 0, 1, glossary); } struct arg_file* arg_file1(const char* shortopts, const char* longopts, const char* datatype, const char* glossary) { return arg_filen(shortopts, longopts, datatype, 1, 1, glossary); } struct arg_file* arg_filen(const char* shortopts, const char* longopts, const char* datatype, int mincount, int maxcount, const char* glossary) { size_t nbytes; struct arg_file* result; int i; /* foolproof things by ensuring maxcount is not less than mincount */ maxcount = (maxcount < mincount) ? mincount : maxcount; nbytes = sizeof(struct arg_file) /* storage for struct arg_file */ + sizeof(char*) * maxcount /* storage for filename[maxcount] array */ + sizeof(char*) * maxcount /* storage for basename[maxcount] array */ + sizeof(char*) * maxcount; /* storage for extension[maxcount] array */ result = (struct arg_file*)xmalloc(nbytes); /* init the arg_hdr struct */ result->hdr.flag = ARG_HASVALUE; result->hdr.shortopts = shortopts; result->hdr.longopts = longopts; result->hdr.glossary = glossary; result->hdr.datatype = datatype ? datatype : ""; result->hdr.mincount = mincount; result->hdr.maxcount = maxcount; result->hdr.parent = result; result->hdr.resetfn = (arg_resetfn*)arg_file_resetfn; result->hdr.scanfn = (arg_scanfn*)arg_file_scanfn; result->hdr.checkfn = (arg_checkfn*)arg_file_checkfn; result->hdr.errorfn = (arg_errorfn*)arg_file_errorfn; /* store the filename,basename,extension arrays immediately after the arg_file struct */ result->filename = (const char**)(result + 1); result->basename = result->filename + maxcount; result->extension = result->basename + maxcount; result->count = 0; /* foolproof the string pointers by initialising them with empty strings */ for (i = 0; i < maxcount; i++) { result->filename[i] = ""; result->basename[i] = ""; result->extension[i] = ""; } ARG_TRACE(("arg_filen() returns %p\n", result)); return result; } /******************************************************************************* * arg_int: Implements the int command-line option * * This file is part of the argtable3 library. * * Copyright (C) 1998-2001,2003-2011,2013 Stewart Heitmann * * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * * Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * Neither the name of STEWART HEITMANN nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL STEWART HEITMANN BE LIABLE FOR ANY DIRECT, * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ******************************************************************************/ #include "argtable3.h" #ifndef ARG_AMALGAMATION #include "argtable3_private.h" #endif #include #include #include static void arg_int_resetfn(struct arg_int* parent) { ARG_TRACE(("%s:resetfn(%p)\n", __FILE__, parent)); parent->count = 0; } /* strtol0x() is like strtol() except that the numeric string is */ /* expected to be prefixed by "0X" where X is a user supplied char. */ /* The string may optionally be prefixed by white space and + or - */ /* as in +0X123 or -0X123. */ /* Once the prefix has been scanned, the remainder of the numeric */ /* string is converted using strtol() with the given base. */ /* eg: to parse hex str="-0X12324", specify X='X' and base=16. */ /* eg: to parse oct str="+0o12324", specify X='O' and base=8. */ /* eg: to parse bin str="-0B01010", specify X='B' and base=2. */ /* Failure of conversion is indicated by result where *endptr==str. */ static long int strtol0X(const char* str, const char** endptr, char X, int base) { long int val; /* stores result */ int s = 1; /* sign is +1 or -1 */ const char* ptr = str; /* ptr to current position in str */ /* skip leading whitespace */ while (isspace((int)(*ptr))) ptr++; /* printf("1) %s\n",ptr); */ /* scan optional sign character */ switch (*ptr) { case '+': ptr++; s = 1; break; case '-': ptr++; s = -1; break; default: s = 1; break; } /* printf("2) %s\n",ptr); */ /* '0X' prefix */ if ((*ptr++) != '0') { /* printf("failed to detect '0'\n"); */ *endptr = str; return 0; } /* printf("3) %s\n",ptr); */ if (toupper(*ptr++) != toupper(X)) { /* printf("failed to detect '%c'\n",X); */ *endptr = str; return 0; } /* printf("4) %s\n",ptr); */ /* attempt conversion on remainder of string using strtol() */ val = strtol(ptr, (char**)endptr, base); if (*endptr == ptr) { /* conversion failed */ *endptr = str; return 0; } /* success */ return s * val; } /* Returns 1 if str matches suffix (case insensitive). */ /* Str may contain trailing whitespace, but nothing else. */ static int detectsuffix(const char* str, const char* suffix) { /* scan pairwise through strings until mismatch detected */ while (toupper(*str) == toupper(*suffix)) { /* printf("'%c' '%c'\n", *str, *suffix); */ /* return 1 (success) if match persists until the string terminator */ if (*str == '\0') return 1; /* next chars */ str++; suffix++; } /* printf("'%c' '%c' mismatch\n", *str, *suffix); */ /* return 0 (fail) if the matching did not consume the entire suffix */ if (*suffix != 0) return 0; /* failed to consume entire suffix */ /* skip any remaining whitespace in str */ while (isspace((int)(*str))) str++; /* return 1 (success) if we have reached end of str else return 0 (fail) */ return (*str == '\0') ? 1 : 0; } static int arg_int_scanfn(struct arg_int* parent, const char* argval) { int errorcode = 0; if (parent->count == parent->hdr.maxcount) { /* maximum number of arguments exceeded */ errorcode = ARG_ERR_MAXCOUNT; } else if (!argval) { /* a valid argument with no argument value was given. */ /* This happens when an optional argument value was invoked. */ /* leave parent argument value unaltered but still count the argument. */ parent->count++; } else { long int val; const char* end; /* attempt to extract hex integer (eg: +0x123) from argval into val conversion */ val = strtol0X(argval, &end, 'X', 16); if (end == argval) { /* hex failed, attempt octal conversion (eg +0o123) */ val = strtol0X(argval, &end, 'O', 8); if (end == argval) { /* octal failed, attempt binary conversion (eg +0B101) */ val = strtol0X(argval, &end, 'B', 2); if (end == argval) { /* binary failed, attempt decimal conversion with no prefix (eg 1234) */ val = strtol(argval, (char**)&end, 10); if (end == argval) { /* all supported number formats failed */ return ARG_ERR_BADINT; } } } } /* Safety check for integer overflow. WARNING: this check */ /* achieves nothing on machines where size(int)==size(long). */ if (val > INT_MAX || val < INT_MIN) errorcode = ARG_ERR_OVERFLOW; /* Detect any suffixes (KB,MB,GB) and multiply argument value appropriately. */ /* We need to be mindful of integer overflows when using such big numbers. */ if (detectsuffix(end, "KB")) /* kilobytes */ { if (val > (INT_MAX / 1024) || val < (INT_MIN / 1024)) errorcode = ARG_ERR_OVERFLOW; /* Overflow would occur if we proceed */ else val *= 1024; /* 1KB = 1024 */ } else if (detectsuffix(end, "MB")) /* megabytes */ { if (val > (INT_MAX / 1048576) || val < (INT_MIN / 1048576)) errorcode = ARG_ERR_OVERFLOW; /* Overflow would occur if we proceed */ else val *= 1048576; /* 1MB = 1024*1024 */ } else if (detectsuffix(end, "GB")) /* gigabytes */ { if (val > (INT_MAX / 1073741824) || val < (INT_MIN / 1073741824)) errorcode = ARG_ERR_OVERFLOW; /* Overflow would occur if we proceed */ else val *= 1073741824; /* 1GB = 1024*1024*1024 */ } else if (!detectsuffix(end, "")) errorcode = ARG_ERR_BADINT; /* invalid suffix detected */ /* if success then store result in parent->ival[] array */ if (errorcode == 0) parent->ival[parent->count++] = (int)val; } /* printf("%s:scanfn(%p,%p) returns %d\n",__FILE__,parent,argval,errorcode); */ return errorcode; } static int arg_int_checkfn(struct arg_int* parent) { int errorcode = (parent->count < parent->hdr.mincount) ? ARG_ERR_MINCOUNT : 0; /*printf("%s:checkfn(%p) returns %d\n",__FILE__,parent,errorcode);*/ return errorcode; } static void arg_int_errorfn(struct arg_int* parent, arg_dstr_t ds, int errorcode, const char* argval, const char* progname) { const char* shortopts = parent->hdr.shortopts; const char* longopts = parent->hdr.longopts; const char* datatype = parent->hdr.datatype; /* make argval NULL safe */ argval = argval ? argval : ""; arg_dstr_catf(ds, "%s: ", progname); switch (errorcode) { case ARG_ERR_MINCOUNT: arg_dstr_cat(ds, "missing option "); arg_print_option_ds(ds, shortopts, longopts, datatype, "\n"); break; case ARG_ERR_MAXCOUNT: arg_dstr_cat(ds, "excess option "); arg_print_option_ds(ds, shortopts, longopts, argval, "\n"); break; case ARG_ERR_BADINT: arg_dstr_catf(ds, "invalid argument \"%s\" to option ", argval); arg_print_option_ds(ds, shortopts, longopts, datatype, "\n"); break; case ARG_ERR_OVERFLOW: arg_dstr_cat(ds, "integer overflow at option "); arg_print_option_ds(ds, shortopts, longopts, datatype, " "); arg_dstr_catf(ds, "(%s is too large)\n", argval); break; } } struct arg_int* arg_int0(const char* shortopts, const char* longopts, const char* datatype, const char* glossary) { return arg_intn(shortopts, longopts, datatype, 0, 1, glossary); } struct arg_int* arg_int1(const char* shortopts, const char* longopts, const char* datatype, const char* glossary) { return arg_intn(shortopts, longopts, datatype, 1, 1, glossary); } struct arg_int* arg_intn(const char* shortopts, const char* longopts, const char* datatype, int mincount, int maxcount, const char* glossary) { size_t nbytes; struct arg_int* result; /* foolproof things by ensuring maxcount is not less than mincount */ maxcount = (maxcount < mincount) ? mincount : maxcount; nbytes = sizeof(struct arg_int) /* storage for struct arg_int */ + maxcount * sizeof(int); /* storage for ival[maxcount] array */ result = (struct arg_int*)xmalloc(nbytes); /* init the arg_hdr struct */ result->hdr.flag = ARG_HASVALUE; result->hdr.shortopts = shortopts; result->hdr.longopts = longopts; result->hdr.datatype = datatype ? datatype : ""; result->hdr.glossary = glossary; result->hdr.mincount = mincount; result->hdr.maxcount = maxcount; result->hdr.parent = result; result->hdr.resetfn = (arg_resetfn*)arg_int_resetfn; result->hdr.scanfn = (arg_scanfn*)arg_int_scanfn; result->hdr.checkfn = (arg_checkfn*)arg_int_checkfn; result->hdr.errorfn = (arg_errorfn*)arg_int_errorfn; /* store the ival[maxcount] array immediately after the arg_int struct */ result->ival = (int*)(result + 1); result->count = 0; ARG_TRACE(("arg_intn() returns %p\n", result)); return result; } /******************************************************************************* * arg_lit: Implements the literature command-line option * * This file is part of the argtable3 library. * * Copyright (C) 1998-2001,2003-2011,2013 Stewart Heitmann * * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * * Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * Neither the name of STEWART HEITMANN nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL STEWART HEITMANN BE LIABLE FOR ANY DIRECT, * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ******************************************************************************/ #include "argtable3.h" #ifndef ARG_AMALGAMATION #include "argtable3_private.h" #endif #include static void arg_lit_resetfn(struct arg_lit* parent) { ARG_TRACE(("%s:resetfn(%p)\n", __FILE__, parent)); parent->count = 0; } static int arg_lit_scanfn(struct arg_lit* parent, const char* argval) { int errorcode = 0; if (parent->count < parent->hdr.maxcount) parent->count++; else errorcode = ARG_ERR_MAXCOUNT; ARG_TRACE(("%s:scanfn(%p,%s) returns %d\n", __FILE__, parent, argval, errorcode)); return errorcode; } static int arg_lit_checkfn(struct arg_lit* parent) { int errorcode = (parent->count < parent->hdr.mincount) ? ARG_ERR_MINCOUNT : 0; ARG_TRACE(("%s:checkfn(%p) returns %d\n", __FILE__, parent, errorcode)); return errorcode; } static void arg_lit_errorfn(struct arg_lit* parent, arg_dstr_t ds, int errorcode, const char* argval, const char* progname) { const char* shortopts = parent->hdr.shortopts; const char* longopts = parent->hdr.longopts; const char* datatype = parent->hdr.datatype; switch (errorcode) { case ARG_ERR_MINCOUNT: arg_dstr_catf(ds, "%s: missing option ", progname); arg_print_option_ds(ds, shortopts, longopts, datatype, "\n"); arg_dstr_cat(ds, "\n"); break; case ARG_ERR_MAXCOUNT: arg_dstr_catf(ds, "%s: extraneous option ", progname); arg_print_option_ds(ds, shortopts, longopts, datatype, "\n"); break; } ARG_TRACE(("%s:errorfn(%p, %p, %d, %s, %s)\n", __FILE__, parent, ds, errorcode, argval, progname)); } struct arg_lit* arg_lit0(const char* shortopts, const char* longopts, const char* glossary) { return arg_litn(shortopts, longopts, 0, 1, glossary); } struct arg_lit* arg_lit1(const char* shortopts, const char* longopts, const char* glossary) { return arg_litn(shortopts, longopts, 1, 1, glossary); } struct arg_lit* arg_litn(const char* shortopts, const char* longopts, int mincount, int maxcount, const char* glossary) { struct arg_lit* result; /* foolproof things by ensuring maxcount is not less than mincount */ maxcount = (maxcount < mincount) ? mincount : maxcount; result = (struct arg_lit*)xmalloc(sizeof(struct arg_lit)); /* init the arg_hdr struct */ result->hdr.flag = 0; result->hdr.shortopts = shortopts; result->hdr.longopts = longopts; result->hdr.datatype = NULL; result->hdr.glossary = glossary; result->hdr.mincount = mincount; result->hdr.maxcount = maxcount; result->hdr.parent = result; result->hdr.resetfn = (arg_resetfn*)arg_lit_resetfn; result->hdr.scanfn = (arg_scanfn*)arg_lit_scanfn; result->hdr.checkfn = (arg_checkfn*)arg_lit_checkfn; result->hdr.errorfn = (arg_errorfn*)arg_lit_errorfn; /* init local variables */ result->count = 0; ARG_TRACE(("arg_litn() returns %p\n", result)); return result; } /******************************************************************************* * arg_rem: Implements the rem command-line option * * This file is part of the argtable3 library. * * Copyright (C) 1998-2001,2003-2011,2013 Stewart Heitmann * * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * * Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * Neither the name of STEWART HEITMANN nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL STEWART HEITMANN BE LIABLE FOR ANY DIRECT, * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ******************************************************************************/ #include "argtable3.h" #ifndef ARG_AMALGAMATION #include "argtable3_private.h" #endif #include struct arg_rem* arg_rem(const char* datatype, const char* glossary) { struct arg_rem* result = (struct arg_rem*)xmalloc(sizeof(struct arg_rem)); result->hdr.flag = 0; result->hdr.shortopts = NULL; result->hdr.longopts = NULL; result->hdr.datatype = datatype; result->hdr.glossary = glossary; result->hdr.mincount = 1; result->hdr.maxcount = 1; result->hdr.parent = result; result->hdr.resetfn = NULL; result->hdr.scanfn = NULL; result->hdr.checkfn = NULL; result->hdr.errorfn = NULL; ARG_TRACE(("arg_rem() returns %p\n", result)); return result; } /******************************************************************************* * arg_rex: Implements the regex command-line option * * This file is part of the argtable3 library. * * Copyright (C) 1998-2001,2003-2011,2013 Stewart Heitmann * * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * * Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * Neither the name of STEWART HEITMANN nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL STEWART HEITMANN BE LIABLE FOR ANY DIRECT, * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ******************************************************************************/ #include "argtable3.h" #ifndef ARG_AMALGAMATION #include "argtable3_private.h" #endif #include #include #ifndef _TREX_H_ #define _TREX_H_ /* * This module uses the T-Rex regular expression library to implement the regex * logic. Here is the copyright notice of the library: * * Copyright (C) 2003-2006 Alberto Demichelis * * This software is provided 'as-is', without any express * or implied warranty. In no event will the authors be held * liable for any damages arising from the use of this software. * * Permission is granted to anyone to use this software for * any purpose, including commercial applications, and to alter * it and redistribute it freely, subject to the following restrictions: * * 1. The origin of this software must not be misrepresented; * you must not claim that you wrote the original software. * If you use this software in a product, an acknowledgment * in the product documentation would be appreciated but * is not required. * * 2. Altered source versions must be plainly marked as such, * and must not be misrepresented as being the original software. * * 3. This notice may not be removed or altered from any * source distribution. */ #ifdef __cplusplus extern "C" { #endif #define TRexChar char #define MAX_CHAR 0xFF #define _TREXC(c) (c) #define trex_strlen strlen #define trex_printf printf #ifndef TREX_API #define TREX_API extern #endif #define TRex_True 1 #define TRex_False 0 #define TREX_ICASE ARG_REX_ICASE typedef unsigned int TRexBool; typedef struct TRex TRex; typedef struct { const TRexChar* begin; int len; } TRexMatch; #ifdef __GNUC__ TREX_API TRex* trex_compile(const TRexChar* pattern, const TRexChar** error, int flags) __attribute__((optimize(0))); #else TREX_API TRex* trex_compile(const TRexChar* pattern, const TRexChar** error, int flags); #endif TREX_API void trex_free(TRex* exp); TREX_API TRexBool trex_match(TRex* exp, const TRexChar* text); TREX_API TRexBool trex_search(TRex* exp, const TRexChar* text, const TRexChar** out_begin, const TRexChar** out_end); TREX_API TRexBool trex_searchrange(TRex* exp, const TRexChar* text_begin, const TRexChar* text_end, const TRexChar** out_begin, const TRexChar** out_end); TREX_API int trex_getsubexpcount(TRex* exp); TREX_API TRexBool trex_getsubexp(TRex* exp, int n, TRexMatch* subexp); #ifdef __cplusplus } #endif #endif struct privhdr { const char* pattern; int flags; }; static void arg_rex_resetfn(struct arg_rex* parent) { ARG_TRACE(("%s:resetfn(%p)\n", __FILE__, parent)); parent->count = 0; } static int arg_rex_scanfn(struct arg_rex* parent, const char* argval) { int errorcode = 0; const TRexChar* error = NULL; TRex* rex = NULL; TRexBool is_match = TRex_False; if (parent->count == parent->hdr.maxcount) { /* maximum number of arguments exceeded */ errorcode = ARG_ERR_MAXCOUNT; } else if (!argval) { /* a valid argument with no argument value was given. */ /* This happens when an optional argument value was invoked. */ /* leave parent argument value unaltered but still count the argument. */ parent->count++; } else { struct privhdr* priv = (struct privhdr*)parent->hdr.priv; /* test the current argument value for a match with the regular expression */ /* if a match is detected, record the argument value in the arg_rex struct */ rex = trex_compile(priv->pattern, &error, priv->flags); is_match = trex_match(rex, argval); if (!is_match) errorcode = ARG_ERR_REGNOMATCH; else parent->sval[parent->count++] = argval; trex_free(rex); } ARG_TRACE(("%s:scanfn(%p) returns %d\n", __FILE__, parent, errorcode)); return errorcode; } static int arg_rex_checkfn(struct arg_rex* parent) { int errorcode = (parent->count < parent->hdr.mincount) ? ARG_ERR_MINCOUNT : 0; #if 0 struct privhdr *priv = (struct privhdr*)parent->hdr.priv; /* free the regex "program" we constructed in resetfn */ regfree(&(priv->regex)); /*printf("%s:checkfn(%p) returns %d\n",__FILE__,parent,errorcode);*/ #endif return errorcode; } static void arg_rex_errorfn(struct arg_rex* parent, arg_dstr_t ds, int errorcode, const char* argval, const char* progname) { const char* shortopts = parent->hdr.shortopts; const char* longopts = parent->hdr.longopts; const char* datatype = parent->hdr.datatype; /* make argval NULL safe */ argval = argval ? argval : ""; arg_dstr_catf(ds, "%s: ", progname); switch (errorcode) { case ARG_ERR_MINCOUNT: arg_dstr_cat(ds, "missing option "); arg_print_option_ds(ds, shortopts, longopts, datatype, "\n"); break; case ARG_ERR_MAXCOUNT: arg_dstr_cat(ds, "excess option "); arg_print_option_ds(ds, shortopts, longopts, argval, "\n"); break; case ARG_ERR_REGNOMATCH: arg_dstr_cat(ds, "illegal value "); arg_print_option_ds(ds, shortopts, longopts, argval, "\n"); break; default: { #if 0 char errbuff[256]; regerror(errorcode, NULL, errbuff, sizeof(errbuff)); printf("%s\n", errbuff); #endif } break; } } struct arg_rex* arg_rex0(const char* shortopts, const char* longopts, const char* pattern, const char* datatype, int flags, const char* glossary) { return arg_rexn(shortopts, longopts, pattern, datatype, 0, 1, flags, glossary); } struct arg_rex* arg_rex1(const char* shortopts, const char* longopts, const char* pattern, const char* datatype, int flags, const char* glossary) { return arg_rexn(shortopts, longopts, pattern, datatype, 1, 1, flags, glossary); } struct arg_rex* arg_rexn(const char* shortopts, const char* longopts, const char* pattern, const char* datatype, int mincount, int maxcount, int flags, const char* glossary) { size_t nbytes; struct arg_rex* result; struct privhdr* priv; int i; const TRexChar* error = NULL; TRex* rex = NULL; if (!pattern) { printf("argtable: ERROR - illegal regular expression pattern \"(NULL)\"\n"); printf("argtable: Bad argument table.\n"); return NULL; } /* foolproof things by ensuring maxcount is not less than mincount */ maxcount = (maxcount < mincount) ? mincount : maxcount; nbytes = sizeof(struct arg_rex) /* storage for struct arg_rex */ + sizeof(struct privhdr) /* storage for private arg_rex data */ + maxcount * sizeof(char*); /* storage for sval[maxcount] array */ /* init the arg_hdr struct */ result = (struct arg_rex*)xmalloc(nbytes); result->hdr.flag = ARG_HASVALUE; result->hdr.shortopts = shortopts; result->hdr.longopts = longopts; result->hdr.datatype = datatype ? datatype : pattern; result->hdr.glossary = glossary; result->hdr.mincount = mincount; result->hdr.maxcount = maxcount; result->hdr.parent = result; result->hdr.resetfn = (arg_resetfn*)arg_rex_resetfn; result->hdr.scanfn = (arg_scanfn*)arg_rex_scanfn; result->hdr.checkfn = (arg_checkfn*)arg_rex_checkfn; result->hdr.errorfn = (arg_errorfn*)arg_rex_errorfn; /* store the arg_rex_priv struct immediately after the arg_rex struct */ result->hdr.priv = result + 1; priv = (struct privhdr*)(result->hdr.priv); priv->pattern = pattern; priv->flags = flags; /* store the sval[maxcount] array immediately after the arg_rex_priv struct */ result->sval = (const char**)(priv + 1); result->count = 0; /* foolproof the string pointers by initializing them to reference empty strings */ for (i = 0; i < maxcount; i++) result->sval[i] = ""; /* here we construct and destroy a regex representation of the regular * expression for no other reason than to force any regex errors to be * trapped now rather than later. If we don't, then errors may go undetected * until an argument is actually parsed. */ rex = trex_compile(priv->pattern, &error, priv->flags); if (rex == NULL) { ARG_LOG(("argtable: %s \"%s\"\n", error ? error : _TREXC("undefined"), priv->pattern)); ARG_LOG(("argtable: Bad argument table.\n")); } trex_free(rex); ARG_TRACE(("arg_rexn() returns %p\n", result)); return result; } /* see copyright notice in trex.h */ #include #include #include #include #ifdef _UINCODE #define scisprint iswprint #define scstrlen wcslen #define scprintf wprintf #define _SC(x) L(x) #else #define scisprint isprint #define scstrlen strlen #define scprintf printf #define _SC(x) (x) #endif #ifdef ARG_REX_DEBUG #include static const TRexChar* g_nnames[] = {_SC("NONE"), _SC("OP_GREEDY"), _SC("OP_OR"), _SC("OP_EXPR"), _SC("OP_NOCAPEXPR"), _SC("OP_DOT"), _SC("OP_CLASS"), _SC("OP_CCLASS"), _SC("OP_NCLASS"), _SC("OP_RANGE"), _SC("OP_CHAR"), _SC("OP_EOL"), _SC("OP_BOL"), _SC("OP_WB")}; #endif #define OP_GREEDY (MAX_CHAR + 1) /* * + ? {n} */ #define OP_OR (MAX_CHAR + 2) #define OP_EXPR (MAX_CHAR + 3) /* parenthesis () */ #define OP_NOCAPEXPR (MAX_CHAR + 4) /* parenthesis (?:) */ #define OP_DOT (MAX_CHAR + 5) #define OP_CLASS (MAX_CHAR + 6) #define OP_CCLASS (MAX_CHAR + 7) #define OP_NCLASS (MAX_CHAR + 8) /* negates class the [^ */ #define OP_RANGE (MAX_CHAR + 9) #define OP_CHAR (MAX_CHAR + 10) #define OP_EOL (MAX_CHAR + 11) #define OP_BOL (MAX_CHAR + 12) #define OP_WB (MAX_CHAR + 13) #define TREX_SYMBOL_ANY_CHAR ('.') #define TREX_SYMBOL_GREEDY_ONE_OR_MORE ('+') #define TREX_SYMBOL_GREEDY_ZERO_OR_MORE ('*') #define TREX_SYMBOL_GREEDY_ZERO_OR_ONE ('?') #define TREX_SYMBOL_BRANCH ('|') #define TREX_SYMBOL_END_OF_STRING ('$') #define TREX_SYMBOL_BEGINNING_OF_STRING ('^') #define TREX_SYMBOL_ESCAPE_CHAR ('\\') typedef int TRexNodeType; typedef struct tagTRexNode { TRexNodeType type; int left; int right; int next; } TRexNode; struct TRex { const TRexChar* _eol; const TRexChar* _bol; const TRexChar* _p; int _first; int _op; TRexNode* _nodes; int _nallocated; int _nsize; int _nsubexpr; TRexMatch* _matches; int _currsubexp; void* _jmpbuf; const TRexChar** _error; int _flags; }; static int trex_list(TRex* exp); static int trex_newnode(TRex* exp, TRexNodeType type) { TRexNode n; int newid; n.type = type; n.next = n.right = n.left = -1; if (type == OP_EXPR) n.right = exp->_nsubexpr++; if (exp->_nallocated < (exp->_nsize + 1)) { exp->_nallocated *= 2; exp->_nodes = (TRexNode*)xrealloc(exp->_nodes, exp->_nallocated * sizeof(TRexNode)); } exp->_nodes[exp->_nsize++] = n; newid = exp->_nsize - 1; return (int)newid; } static void trex_error(TRex* exp, const TRexChar* error) { if (exp->_error) *exp->_error = error; longjmp(*((jmp_buf*)exp->_jmpbuf), -1); } static void trex_expect(TRex* exp, int n) { if ((*exp->_p) != n) trex_error(exp, _SC("expected paren")); exp->_p++; } static TRexChar trex_escapechar(TRex* exp) { if (*exp->_p == TREX_SYMBOL_ESCAPE_CHAR) { exp->_p++; switch (*exp->_p) { case 'v': exp->_p++; return '\v'; case 'n': exp->_p++; return '\n'; case 't': exp->_p++; return '\t'; case 'r': exp->_p++; return '\r'; case 'f': exp->_p++; return '\f'; default: return (*exp->_p++); } } else if (!scisprint((int)(*exp->_p))) trex_error(exp, _SC("letter expected")); return (*exp->_p++); } static int trex_charclass(TRex* exp, int classid) { int n = trex_newnode(exp, OP_CCLASS); exp->_nodes[n].left = classid; return n; } static int trex_charnode(TRex* exp, TRexBool isclass) { TRexChar t; if (*exp->_p == TREX_SYMBOL_ESCAPE_CHAR) { exp->_p++; switch (*exp->_p) { case 'n': exp->_p++; return trex_newnode(exp, '\n'); case 't': exp->_p++; return trex_newnode(exp, '\t'); case 'r': exp->_p++; return trex_newnode(exp, '\r'); case 'f': exp->_p++; return trex_newnode(exp, '\f'); case 'v': exp->_p++; return trex_newnode(exp, '\v'); case 'a': case 'A': case 'w': case 'W': case 's': case 'S': case 'd': case 'D': case 'x': case 'X': case 'c': case 'C': case 'p': case 'P': case 'l': case 'u': { t = *exp->_p; exp->_p++; return trex_charclass(exp, t); } case 'b': case 'B': if (!isclass) { int node = trex_newnode(exp, OP_WB); exp->_nodes[node].left = *exp->_p; exp->_p++; return node; } /* fall through */ default: t = *exp->_p; exp->_p++; return trex_newnode(exp, t); } } else if (!scisprint((int)(*exp->_p))) { trex_error(exp, _SC("letter expected")); } t = *exp->_p; exp->_p++; return trex_newnode(exp, t); } static int trex_class(TRex* exp) { int ret = -1; int first = -1, chain; if (*exp->_p == TREX_SYMBOL_BEGINNING_OF_STRING) { ret = trex_newnode(exp, OP_NCLASS); exp->_p++; } else ret = trex_newnode(exp, OP_CLASS); if (*exp->_p == ']') trex_error(exp, _SC("empty class")); chain = ret; while (*exp->_p != ']' && exp->_p != exp->_eol) { if (*exp->_p == '-' && first != -1) { int r, t; if (*exp->_p++ == ']') trex_error(exp, _SC("unfinished range")); r = trex_newnode(exp, OP_RANGE); if (first > *exp->_p) trex_error(exp, _SC("invalid range")); if (exp->_nodes[first].type == OP_CCLASS) trex_error(exp, _SC("cannot use character classes in ranges")); exp->_nodes[r].left = exp->_nodes[first].type; t = trex_escapechar(exp); exp->_nodes[r].right = t; exp->_nodes[chain].next = r; chain = r; first = -1; } else { if (first != -1) { int c = first; exp->_nodes[chain].next = c; chain = c; first = trex_charnode(exp, TRex_True); } else { first = trex_charnode(exp, TRex_True); } } } if (first != -1) { int c = first; exp->_nodes[chain].next = c; chain = c; first = -1; } /* hack? */ exp->_nodes[ret].left = exp->_nodes[ret].next; exp->_nodes[ret].next = -1; return ret; } static int trex_parsenumber(TRex* exp) { int ret = *exp->_p - '0'; int positions = 10; exp->_p++; while (isdigit((int)(*exp->_p))) { ret = ret * 10 + (*exp->_p++ - '0'); if (positions == 1000000000) trex_error(exp, _SC("overflow in numeric constant")); positions *= 10; }; return ret; } static int trex_element(TRex* exp) { int ret = -1; switch (*exp->_p) { case '(': { int expr, newn; exp->_p++; if (*exp->_p == '?') { exp->_p++; trex_expect(exp, ':'); expr = trex_newnode(exp, OP_NOCAPEXPR); } else expr = trex_newnode(exp, OP_EXPR); newn = trex_list(exp); exp->_nodes[expr].left = newn; ret = expr; trex_expect(exp, ')'); } break; case '[': exp->_p++; ret = trex_class(exp); trex_expect(exp, ']'); break; case TREX_SYMBOL_END_OF_STRING: exp->_p++; ret = trex_newnode(exp, OP_EOL); break; case TREX_SYMBOL_ANY_CHAR: exp->_p++; ret = trex_newnode(exp, OP_DOT); break; default: ret = trex_charnode(exp, TRex_False); break; } { TRexBool isgreedy = TRex_False; unsigned short p0 = 0, p1 = 0; switch (*exp->_p) { case TREX_SYMBOL_GREEDY_ZERO_OR_MORE: p0 = 0; p1 = 0xFFFF; exp->_p++; isgreedy = TRex_True; break; case TREX_SYMBOL_GREEDY_ONE_OR_MORE: p0 = 1; p1 = 0xFFFF; exp->_p++; isgreedy = TRex_True; break; case TREX_SYMBOL_GREEDY_ZERO_OR_ONE: p0 = 0; p1 = 1; exp->_p++; isgreedy = TRex_True; break; case '{': exp->_p++; if (!isdigit((int)(*exp->_p))) trex_error(exp, _SC("number expected")); p0 = (unsigned short)trex_parsenumber(exp); /*******************************/ switch (*exp->_p) { case '}': p1 = p0; exp->_p++; break; case ',': exp->_p++; p1 = 0xFFFF; if (isdigit((int)(*exp->_p))) { p1 = (unsigned short)trex_parsenumber(exp); } trex_expect(exp, '}'); break; default: trex_error(exp, _SC(", or } expected")); } /*******************************/ isgreedy = TRex_True; break; } if (isgreedy) { int nnode = trex_newnode(exp, OP_GREEDY); exp->_nodes[nnode].left = ret; exp->_nodes[nnode].right = ((p0) << 16) | p1; ret = nnode; } } if ((*exp->_p != TREX_SYMBOL_BRANCH) && (*exp->_p != ')') && (*exp->_p != TREX_SYMBOL_GREEDY_ZERO_OR_MORE) && (*exp->_p != TREX_SYMBOL_GREEDY_ONE_OR_MORE) && (*exp->_p != '\0')) { int nnode = trex_element(exp); exp->_nodes[ret].next = nnode; } return ret; } static int trex_list(TRex* exp) { int ret = -1, e; if (*exp->_p == TREX_SYMBOL_BEGINNING_OF_STRING) { exp->_p++; ret = trex_newnode(exp, OP_BOL); } e = trex_element(exp); if (ret != -1) { exp->_nodes[ret].next = e; } else ret = e; if (*exp->_p == TREX_SYMBOL_BRANCH) { int temp, tright; exp->_p++; temp = trex_newnode(exp, OP_OR); exp->_nodes[temp].left = ret; tright = trex_list(exp); exp->_nodes[temp].right = tright; ret = temp; } return ret; } static TRexBool trex_matchcclass(int cclass, TRexChar c) { switch (cclass) { case 'a': return isalpha(c) ? TRex_True : TRex_False; case 'A': return !isalpha(c) ? TRex_True : TRex_False; case 'w': return (isalnum(c) || c == '_') ? TRex_True : TRex_False; case 'W': return (!isalnum(c) && c != '_') ? TRex_True : TRex_False; case 's': return isspace(c) ? TRex_True : TRex_False; case 'S': return !isspace(c) ? TRex_True : TRex_False; case 'd': return isdigit(c) ? TRex_True : TRex_False; case 'D': return !isdigit(c) ? TRex_True : TRex_False; case 'x': return isxdigit(c) ? TRex_True : TRex_False; case 'X': return !isxdigit(c) ? TRex_True : TRex_False; case 'c': return iscntrl(c) ? TRex_True : TRex_False; case 'C': return !iscntrl(c) ? TRex_True : TRex_False; case 'p': return ispunct(c) ? TRex_True : TRex_False; case 'P': return !ispunct(c) ? TRex_True : TRex_False; case 'l': return islower(c) ? TRex_True : TRex_False; case 'u': return isupper(c) ? TRex_True : TRex_False; } return TRex_False; /*cannot happen*/ } static TRexBool trex_matchclass(TRex* exp, TRexNode* node, TRexChar c) { do { switch (node->type) { case OP_RANGE: if (exp->_flags & TREX_ICASE) { if (c >= toupper(node->left) && c <= toupper(node->right)) return TRex_True; if (c >= tolower(node->left) && c <= tolower(node->right)) return TRex_True; } else { if (c >= node->left && c <= node->right) return TRex_True; } break; case OP_CCLASS: if (trex_matchcclass(node->left, c)) return TRex_True; break; default: if (exp->_flags & TREX_ICASE) { if (c == tolower(node->type) || c == toupper(node->type)) return TRex_True; } else { if (c == node->type) return TRex_True; } } } while ((node->next != -1) && ((node = &exp->_nodes[node->next]) != NULL)); return TRex_False; } static const TRexChar* trex_matchnode(TRex* exp, TRexNode* node, const TRexChar* str, TRexNode* next) { TRexNodeType type = node->type; switch (type) { case OP_GREEDY: { /* TRexNode *greedystop = (node->next != -1) ? &exp->_nodes[node->next] : NULL; */ TRexNode* greedystop = NULL; int p0 = (node->right >> 16) & 0x0000FFFF, p1 = node->right & 0x0000FFFF, nmaches = 0; const TRexChar *s = str, *good = str; if (node->next != -1) { greedystop = &exp->_nodes[node->next]; } else { greedystop = next; } while ((nmaches == 0xFFFF || nmaches < p1)) { const TRexChar* stop; if ((s = trex_matchnode(exp, &exp->_nodes[node->left], s, greedystop)) == NULL) break; nmaches++; good = s; if (greedystop) { /* checks that 0 matches satisfy the expression(if so skips) */ /* if not would always stop(for instance if is a '?') */ if (greedystop->type != OP_GREEDY || (greedystop->type == OP_GREEDY && ((greedystop->right >> 16) & 0x0000FFFF) != 0)) { TRexNode* gnext = NULL; if (greedystop->next != -1) { gnext = &exp->_nodes[greedystop->next]; } else if (next && next->next != -1) { gnext = &exp->_nodes[next->next]; } stop = trex_matchnode(exp, greedystop, s, gnext); if (stop) { /* if satisfied stop it */ if (p0 == p1 && p0 == nmaches) break; else if (nmaches >= p0 && p1 == 0xFFFF) break; else if (nmaches >= p0 && nmaches <= p1) break; } } } if (s >= exp->_eol) break; } if (p0 == p1 && p0 == nmaches) return good; else if (nmaches >= p0 && p1 == 0xFFFF) return good; else if (nmaches >= p0 && nmaches <= p1) return good; return NULL; } case OP_OR: { const TRexChar* asd = str; TRexNode* temp = &exp->_nodes[node->left]; while ((asd = trex_matchnode(exp, temp, asd, NULL)) != NULL) { if (temp->next != -1) temp = &exp->_nodes[temp->next]; else return asd; } asd = str; temp = &exp->_nodes[node->right]; while ((asd = trex_matchnode(exp, temp, asd, NULL)) != NULL) { if (temp->next != -1) temp = &exp->_nodes[temp->next]; else return asd; } return NULL; break; } case OP_EXPR: case OP_NOCAPEXPR: { TRexNode* n = &exp->_nodes[node->left]; const TRexChar* cur = str; int capture = -1; if (node->type != OP_NOCAPEXPR && node->right == exp->_currsubexp) { capture = exp->_currsubexp; exp->_matches[capture].begin = cur; exp->_currsubexp++; } do { TRexNode* subnext = NULL; if (n->next != -1) { subnext = &exp->_nodes[n->next]; } else { subnext = next; } if ((cur = trex_matchnode(exp, n, cur, subnext)) == NULL) { if (capture != -1) { exp->_matches[capture].begin = 0; exp->_matches[capture].len = 0; } return NULL; } } while ((n->next != -1) && ((n = &exp->_nodes[n->next]) != NULL)); if (capture != -1) exp->_matches[capture].len = (int)(cur - exp->_matches[capture].begin); return cur; } case OP_WB: if ((str == exp->_bol && !isspace((int)(*str))) || (str == exp->_eol && !isspace((int)(*(str - 1)))) || (!isspace((int)(*str)) && isspace((int)(*(str + 1)))) || (isspace((int)(*str)) && !isspace((int)(*(str + 1))))) { return (node->left == 'b') ? str : NULL; } return (node->left == 'b') ? NULL : str; case OP_BOL: if (str == exp->_bol) return str; return NULL; case OP_EOL: if (str == exp->_eol) return str; return NULL; case OP_DOT: { str++; } return str; case OP_NCLASS: case OP_CLASS: if (trex_matchclass(exp, &exp->_nodes[node->left], *str) ? (type == OP_CLASS ? TRex_True : TRex_False) : (type == OP_NCLASS ? TRex_True : TRex_False)) { str++; return str; } return NULL; case OP_CCLASS: if (trex_matchcclass(node->left, *str)) { str++; return str; } return NULL; default: /* char */ if (exp->_flags & TREX_ICASE) { if (*str != tolower(node->type) && *str != toupper(node->type)) return NULL; } else { if (*str != node->type) return NULL; } str++; return str; } } /* public api */ TRex* trex_compile(const TRexChar* pattern, const TRexChar** error, int flags) { TRex* exp = (TRex*)xmalloc(sizeof(TRex)); exp->_eol = exp->_bol = NULL; exp->_p = pattern; exp->_nallocated = (int)scstrlen(pattern) * sizeof(TRexChar); exp->_nodes = (TRexNode*)xmalloc(exp->_nallocated * sizeof(TRexNode)); exp->_nsize = 0; exp->_matches = 0; exp->_nsubexpr = 0; exp->_first = trex_newnode(exp, OP_EXPR); exp->_error = error; exp->_jmpbuf = xmalloc(sizeof(jmp_buf)); exp->_flags = flags; if (setjmp(*((jmp_buf*)exp->_jmpbuf)) == 0) { int res = trex_list(exp); exp->_nodes[exp->_first].left = res; if (*exp->_p != '\0') trex_error(exp, _SC("unexpected character")); #ifdef ARG_REX_DEBUG { int nsize, i; nsize = exp->_nsize; scprintf(_SC("\n")); for (i = 0; i < nsize; i++) { if (exp->_nodes[i].type > MAX_CHAR) scprintf(_SC("[%02d] %10s "), i, g_nnames[exp->_nodes[i].type - MAX_CHAR]); else scprintf(_SC("[%02d] %10c "), i, exp->_nodes[i].type); scprintf(_SC("left %02d right %02d next %02d\n"), exp->_nodes[i].left, exp->_nodes[i].right, exp->_nodes[i].next); } scprintf(_SC("\n")); } #endif exp->_matches = (TRexMatch*)xmalloc(exp->_nsubexpr * sizeof(TRexMatch)); memset(exp->_matches, 0, exp->_nsubexpr * sizeof(TRexMatch)); } else { trex_free(exp); return NULL; } return exp; } void trex_free(TRex* exp) { if (exp) { xfree(exp->_nodes); xfree(exp->_jmpbuf); xfree(exp->_matches); xfree(exp); } } TRexBool trex_match(TRex* exp, const TRexChar* text) { const TRexChar* res = NULL; exp->_bol = text; exp->_eol = text + scstrlen(text); exp->_currsubexp = 0; res = trex_matchnode(exp, exp->_nodes, text, NULL); if (res == NULL || res != exp->_eol) return TRex_False; return TRex_True; } TRexBool trex_searchrange(TRex* exp, const TRexChar* text_begin, const TRexChar* text_end, const TRexChar** out_begin, const TRexChar** out_end) { const TRexChar* cur = NULL; int node = exp->_first; if (text_begin >= text_end) return TRex_False; exp->_bol = text_begin; exp->_eol = text_end; do { cur = text_begin; while (node != -1) { exp->_currsubexp = 0; cur = trex_matchnode(exp, &exp->_nodes[node], cur, NULL); if (!cur) break; node = exp->_nodes[node].next; } text_begin++; } while (cur == NULL && text_begin != text_end); if (cur == NULL) return TRex_False; --text_begin; if (out_begin) *out_begin = text_begin; if (out_end) *out_end = cur; return TRex_True; } TRexBool trex_search(TRex* exp, const TRexChar* text, const TRexChar** out_begin, const TRexChar** out_end) { return trex_searchrange(exp, text, text + scstrlen(text), out_begin, out_end); } int trex_getsubexpcount(TRex* exp) { return exp->_nsubexpr; } TRexBool trex_getsubexp(TRex* exp, int n, TRexMatch* subexp) { if (n < 0 || n >= exp->_nsubexpr) return TRex_False; *subexp = exp->_matches[n]; return TRex_True; } /******************************************************************************* * arg_str: Implements the str command-line option * * This file is part of the argtable3 library. * * Copyright (C) 1998-2001,2003-2011,2013 Stewart Heitmann * * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * * Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * Neither the name of STEWART HEITMANN nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL STEWART HEITMANN BE LIABLE FOR ANY DIRECT, * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ******************************************************************************/ #include "argtable3.h" #ifndef ARG_AMALGAMATION #include "argtable3_private.h" #endif #include static void arg_str_resetfn(struct arg_str* parent) { int i; ARG_TRACE(("%s:resetfn(%p)\n", __FILE__, parent)); for (i = 0; i < parent->count; i++) { parent->sval[i] = ""; } parent->count = 0; } static int arg_str_scanfn(struct arg_str* parent, const char* argval) { int errorcode = 0; if (parent->count == parent->hdr.maxcount) { /* maximum number of arguments exceeded */ errorcode = ARG_ERR_MAXCOUNT; } else if (!argval) { /* a valid argument with no argument value was given. */ /* This happens when an optional argument value was invoked. */ /* leave parent argument value unaltered but still count the argument. */ parent->count++; } else { parent->sval[parent->count++] = argval; } ARG_TRACE(("%s:scanfn(%p) returns %d\n", __FILE__, parent, errorcode)); return errorcode; } static int arg_str_checkfn(struct arg_str* parent) { int errorcode = (parent->count < parent->hdr.mincount) ? ARG_ERR_MINCOUNT : 0; ARG_TRACE(("%s:checkfn(%p) returns %d\n", __FILE__, parent, errorcode)); return errorcode; } static void arg_str_errorfn(struct arg_str* parent, arg_dstr_t ds, int errorcode, const char* argval, const char* progname) { const char* shortopts = parent->hdr.shortopts; const char* longopts = parent->hdr.longopts; const char* datatype = parent->hdr.datatype; /* make argval NULL safe */ argval = argval ? argval : ""; arg_dstr_catf(ds, "%s: ", progname); switch (errorcode) { case ARG_ERR_MINCOUNT: arg_dstr_cat(ds, "missing option "); arg_print_option_ds(ds, shortopts, longopts, datatype, "\n"); break; case ARG_ERR_MAXCOUNT: arg_dstr_cat(ds, "excess option "); arg_print_option_ds(ds, shortopts, longopts, argval, "\n"); break; } } struct arg_str* arg_str0(const char* shortopts, const char* longopts, const char* datatype, const char* glossary) { return arg_strn(shortopts, longopts, datatype, 0, 1, glossary); } struct arg_str* arg_str1(const char* shortopts, const char* longopts, const char* datatype, const char* glossary) { return arg_strn(shortopts, longopts, datatype, 1, 1, glossary); } struct arg_str* arg_strn(const char* shortopts, const char* longopts, const char* datatype, int mincount, int maxcount, const char* glossary) { size_t nbytes; struct arg_str* result; int i; /* should not allow this stupid error */ /* we should return an error code warning this logic error */ /* foolproof things by ensuring maxcount is not less than mincount */ maxcount = (maxcount < mincount) ? mincount : maxcount; nbytes = sizeof(struct arg_str) /* storage for struct arg_str */ + maxcount * sizeof(char*); /* storage for sval[maxcount] array */ result = (struct arg_str*)xmalloc(nbytes); /* init the arg_hdr struct */ result->hdr.flag = ARG_HASVALUE; result->hdr.shortopts = shortopts; result->hdr.longopts = longopts; result->hdr.datatype = datatype ? datatype : ""; result->hdr.glossary = glossary; result->hdr.mincount = mincount; result->hdr.maxcount = maxcount; result->hdr.parent = result; result->hdr.resetfn = (arg_resetfn*)arg_str_resetfn; result->hdr.scanfn = (arg_scanfn*)arg_str_scanfn; result->hdr.checkfn = (arg_checkfn*)arg_str_checkfn; result->hdr.errorfn = (arg_errorfn*)arg_str_errorfn; /* store the sval[maxcount] array immediately after the arg_str struct */ result->sval = (const char**)(result + 1); result->count = 0; /* foolproof the string pointers by initializing them to reference empty strings */ for (i = 0; i < maxcount; i++) result->sval[i] = ""; ARG_TRACE(("arg_strn() returns %p\n", result)); return result; } /******************************************************************************* * arg_cmd: Provides the sub-command mechanism * * This file is part of the argtable3 library. * * Copyright (C) 2013-2019 Tom G. Huang * * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * * Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * Neither the name of STEWART HEITMANN nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL STEWART HEITMANN BE LIABLE FOR ANY DIRECT, * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ******************************************************************************/ #include "argtable3.h" #ifndef ARG_AMALGAMATION #include "argtable3_private.h" #endif #include #include #include #define MAX_MODULE_VERSION_SIZE 128 static arg_hashtable_t* s_hashtable = NULL; static char* s_module_name = NULL; static int s_mod_ver_major = 0; static int s_mod_ver_minor = 0; static int s_mod_ver_patch = 0; static char* s_mod_ver_tag = NULL; static char* s_mod_ver = NULL; void arg_set_module_name(const char* name) { size_t slen; xfree(s_module_name); slen = strlen(name); s_module_name = (char*)xmalloc(slen + 1); memset(s_module_name, 0, slen + 1); #if (defined(__STDC_LIB_EXT1__) && defined(__STDC_WANT_LIB_EXT1__)) || (defined(__STDC_SECURE_LIB__) && defined(__STDC_WANT_SECURE_LIB__)) strncpy_s(s_module_name, slen + 1, name, slen); #else memcpy(s_module_name, name, slen); #endif } void arg_set_module_version(int major, int minor, int patch, const char* tag) { size_t slen_tag, slen_ds; arg_dstr_t ds; s_mod_ver_major = major; s_mod_ver_minor = minor; s_mod_ver_patch = patch; xfree(s_mod_ver_tag); slen_tag = strlen(tag); s_mod_ver_tag = (char*)xmalloc(slen_tag + 1); memset(s_mod_ver_tag, 0, slen_tag + 1); #if (defined(__STDC_LIB_EXT1__) && defined(__STDC_WANT_LIB_EXT1__)) || (defined(__STDC_SECURE_LIB__) && defined(__STDC_WANT_SECURE_LIB__)) strncpy_s(s_mod_ver_tag, slen_tag + 1, tag, slen_tag); #else memcpy(s_mod_ver_tag, tag, slen_tag); #endif ds = arg_dstr_create(); arg_dstr_catf(ds, "%d.", s_mod_ver_major); arg_dstr_catf(ds, "%d.", s_mod_ver_minor); arg_dstr_catf(ds, "%d.", s_mod_ver_patch); arg_dstr_cat(ds, s_mod_ver_tag); xfree(s_mod_ver); slen_ds = strlen(arg_dstr_cstr(ds)); s_mod_ver = (char*)xmalloc(slen_ds + 1); memset(s_mod_ver, 0, slen_ds + 1); #if (defined(__STDC_LIB_EXT1__) && defined(__STDC_WANT_LIB_EXT1__)) || (defined(__STDC_SECURE_LIB__) && defined(__STDC_WANT_SECURE_LIB__)) strncpy_s(s_mod_ver, slen_ds + 1, arg_dstr_cstr(ds), slen_ds); #else memcpy(s_mod_ver, arg_dstr_cstr(ds), slen_ds); #endif arg_dstr_destroy(ds); } static unsigned int hash_key(const void* key) { const char* str = (const char*)key; int c; unsigned int hash = 5381; while ((c = *str++) != 0) hash = ((hash << 5) + hash) + c; /* hash * 33 + c */ return hash; } static int equal_keys(const void* key1, const void* key2) { char* k1 = (char*)key1; char* k2 = (char*)key2; return (0 == strcmp(k1, k2)); } void arg_cmd_init(void) { s_hashtable = arg_hashtable_create(32, hash_key, equal_keys); } void arg_cmd_uninit(void) { arg_hashtable_destroy(s_hashtable, 1); } void arg_cmd_register(const char* name, arg_cmdfn* proc, const char* description) { arg_cmd_info_t* cmd_info; size_t slen_name; void* k; assert(strlen(name) < ARG_CMD_NAME_LEN); assert(strlen(description) < ARG_CMD_DESCRIPTION_LEN); /* Check if the command already exists. */ /* If the command exists, replace the existing command. */ /* If the command doesn't exist, insert the command. */ cmd_info = (arg_cmd_info_t*)arg_hashtable_search(s_hashtable, name); if (cmd_info) { arg_hashtable_remove(s_hashtable, name); cmd_info = NULL; } cmd_info = (arg_cmd_info_t*)xmalloc(sizeof(arg_cmd_info_t)); memset(cmd_info, 0, sizeof(arg_cmd_info_t)); #if (defined(__STDC_LIB_EXT1__) && defined(__STDC_WANT_LIB_EXT1__)) || (defined(__STDC_SECURE_LIB__) && defined(__STDC_WANT_SECURE_LIB__)) strncpy_s(cmd_info->name, ARG_CMD_NAME_LEN, name, strlen(name)); strncpy_s(cmd_info->description, ARG_CMD_DESCRIPTION_LEN, description, strlen(description)); #else memcpy(cmd_info->name, name, strlen(name)); memcpy(cmd_info->description, description, strlen(description)); #endif cmd_info->proc = proc; slen_name = strlen(name); k = xmalloc(slen_name + 1); memset(k, 0, slen_name + 1); #if (defined(__STDC_LIB_EXT1__) && defined(__STDC_WANT_LIB_EXT1__)) || (defined(__STDC_SECURE_LIB__) && defined(__STDC_WANT_SECURE_LIB__)) strncpy_s((char*)k, slen_name + 1, name, slen_name); #else memcpy((char*)k, name, slen_name); #endif arg_hashtable_insert(s_hashtable, k, cmd_info); } void arg_cmd_unregister(const char* name) { arg_hashtable_remove(s_hashtable, name); } int arg_cmd_dispatch(const char* name, int argc, char* argv[], arg_dstr_t res) { arg_cmd_info_t* cmd_info = arg_cmd_info(name); assert(cmd_info != NULL); assert(cmd_info->proc != NULL); return cmd_info->proc(argc, argv, res); } arg_cmd_info_t* arg_cmd_info(const char* name) { return (arg_cmd_info_t*)arg_hashtable_search(s_hashtable, name); } unsigned int arg_cmd_count(void) { return arg_hashtable_count(s_hashtable); } arg_cmd_itr_t arg_cmd_itr_create(void) { return (arg_cmd_itr_t)arg_hashtable_itr_create(s_hashtable); } int arg_cmd_itr_advance(arg_cmd_itr_t itr) { return arg_hashtable_itr_advance((arg_hashtable_itr_t*)itr); } char* arg_cmd_itr_key(arg_cmd_itr_t itr) { return (char*)arg_hashtable_itr_key((arg_hashtable_itr_t*)itr); } arg_cmd_info_t* arg_cmd_itr_value(arg_cmd_itr_t itr) { return (arg_cmd_info_t*)arg_hashtable_itr_value((arg_hashtable_itr_t*)itr); } void arg_cmd_itr_destroy(arg_cmd_itr_t itr) { arg_hashtable_itr_destroy((arg_hashtable_itr_t*)itr); } int arg_cmd_itr_search(arg_cmd_itr_t itr, void* k) { return arg_hashtable_itr_search((arg_hashtable_itr_t*)itr, s_hashtable, k); } static const char* module_name(void) { if (s_module_name == NULL || strlen(s_module_name) == 0) return ""; return s_module_name; } static const char* module_version(void) { if (s_mod_ver == NULL || strlen(s_mod_ver) == 0) return "0.0.0.0"; return s_mod_ver; } void arg_make_get_help_msg(arg_dstr_t res) { arg_dstr_catf(res, "%s v%s\n", module_name(), module_version()); arg_dstr_catf(res, "Please type '%s help' to get more information.\n", module_name()); } void arg_make_help_msg(arg_dstr_t ds, char* cmd_name, void** argtable) { arg_cmd_info_t* cmd_info = (arg_cmd_info_t*)arg_hashtable_search(s_hashtable, cmd_name); if (cmd_info) { arg_dstr_catf(ds, "%s: %s\n", cmd_name, cmd_info->description); } arg_dstr_cat(ds, "Usage:\n"); arg_dstr_catf(ds, " %s", module_name()); arg_print_syntaxv_ds(ds, argtable, "\n \nAvailable options:\n"); arg_print_glossary_ds(ds, argtable, " %-23s %s\n"); arg_dstr_cat(ds, "\n"); } void arg_make_syntax_err_msg(arg_dstr_t ds, void** argtable, struct arg_end* end) { arg_print_errors_ds(ds, end, module_name()); arg_dstr_cat(ds, "Usage: \n"); arg_dstr_catf(ds, " %s", module_name()); arg_print_syntaxv_ds(ds, argtable, "\n"); arg_dstr_cat(ds, "\n"); } int arg_make_syntax_err_help_msg(arg_dstr_t ds, char* name, int help, int nerrors, void** argtable, struct arg_end* end, int* exitcode) { /* help handling * note: '-h|--help' takes precedence over error reporting */ if (help > 0) { arg_make_help_msg(ds, name, argtable); *exitcode = EXIT_SUCCESS; return 1; } /* syntax error handling */ if (nerrors > 0) { arg_make_syntax_err_msg(ds, argtable, end); *exitcode = EXIT_FAILURE; return 1; } return 0; } /******************************************************************************* * argtable3: Implements the main interfaces of the library * * This file is part of the argtable3 library. * * Copyright (C) 1998-2001,2003-2011,2013 Stewart Heitmann * * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * * Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * Neither the name of STEWART HEITMANN nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL STEWART HEITMANN BE LIABLE FOR ANY DIRECT, * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ******************************************************************************/ #include "argtable3.h" #ifndef ARG_AMALGAMATION #include "argtable3_private.h" #if ARG_REPLACE_GETOPT == 1 #include "arg_getopt.h" #else #include #endif #else #if ARG_REPLACE_GETOPT == 0 #include #endif #endif #ifdef _WIN32 #define WIN32_LEAN_AND_MEAN #include #undef WIN32_LEAN_AND_MEAN #endif #include #include #include #include #include static void arg_register_error(struct arg_end* end, void* parent, int error, const char* argval) { /* printf("arg_register_error(%p,%p,%d,%s)\n",end,parent,error,argval); */ if (end->count < end->hdr.maxcount) { end->error[end->count] = error; end->parent[end->count] = parent; end->argval[end->count] = argval; end->count++; } else { end->error[end->hdr.maxcount - 1] = ARG_ELIMIT; end->parent[end->hdr.maxcount - 1] = end; end->argval[end->hdr.maxcount - 1] = NULL; } } /* * Return index of first table entry with a matching short option * or -1 if no match was found. */ static int find_shortoption(struct arg_hdr** table, char shortopt) { int tabindex; for (tabindex = 0; !(table[tabindex]->flag & ARG_TERMINATOR); tabindex++) { if (table[tabindex]->shortopts && strchr(table[tabindex]->shortopts, shortopt)) return tabindex; } return -1; } struct longoptions { int getoptval; int noptions; struct option* options; }; #if 0 static void dump_longoptions(struct longoptions * longoptions) { int i; printf("getoptval = %d\n", longoptions->getoptval); printf("noptions = %d\n", longoptions->noptions); for (i = 0; i < longoptions->noptions; i++) { printf("options[%d].name = \"%s\"\n", i, longoptions->options[i].name); printf("options[%d].has_arg = %d\n", i, longoptions->options[i].has_arg); printf("options[%d].flag = %p\n", i, longoptions->options[i].flag); printf("options[%d].val = %d\n", i, longoptions->options[i].val); } } #endif static struct longoptions* alloc_longoptions(struct arg_hdr** table) { struct longoptions* result; size_t nbytes; int noptions = 1; size_t longoptlen = 0; int tabindex; int option_index = 0; char* store; /* * Determine the total number of option structs required * by counting the number of comma separated long options * in all table entries and return the count in noptions. * note: noptions starts at 1 not 0 because we getoptlong * requires a NULL option entry to terminate the option array. * While we are at it, count the number of chars required * to store private copies of all the longoption strings * and return that count in logoptlen. */ tabindex = 0; do { const char* longopts = table[tabindex]->longopts; longoptlen += (longopts ? strlen(longopts) : 0) + 1; while (longopts) { noptions++; longopts = strchr(longopts + 1, ','); } } while (!(table[tabindex++]->flag & ARG_TERMINATOR)); /*printf("%d long options consuming %d chars in total\n",noptions,longoptlen);*/ /* allocate storage for return data structure as: */ /* (struct longoptions) + (struct options)[noptions] + char[longoptlen] */ nbytes = sizeof(struct longoptions) + sizeof(struct option) * noptions + longoptlen; result = (struct longoptions*)xmalloc(nbytes); result->getoptval = 0; result->noptions = noptions; result->options = (struct option*)(result + 1); store = (char*)(result->options + noptions); for (tabindex = 0; !(table[tabindex]->flag & ARG_TERMINATOR); tabindex++) { const char* longopts = table[tabindex]->longopts; while (longopts && *longopts) { char* storestart = store; /* copy progressive longopt strings into the store */ while (*longopts != 0 && *longopts != ',') *store++ = *longopts++; *store++ = 0; if (*longopts == ',') longopts++; /*fprintf(stderr,"storestart=\"%s\"\n",storestart);*/ result->options[option_index].name = storestart; result->options[option_index].flag = &(result->getoptval); result->options[option_index].val = tabindex; if (table[tabindex]->flag & ARG_HASOPTVALUE) result->options[option_index].has_arg = 2; else if (table[tabindex]->flag & ARG_HASVALUE) result->options[option_index].has_arg = 1; else result->options[option_index].has_arg = 0; option_index++; } } /* terminate the options array with a zero-filled entry */ result->options[option_index].name = 0; result->options[option_index].has_arg = 0; result->options[option_index].flag = 0; result->options[option_index].val = 0; /*dump_longoptions(result);*/ return result; } static char* alloc_shortoptions(struct arg_hdr** table) { char* result; size_t len = 2; int tabindex; char* res; /* determine the total number of option chars required */ for (tabindex = 0; !(table[tabindex]->flag & ARG_TERMINATOR); tabindex++) { struct arg_hdr* hdr = table[tabindex]; len += 3 * (hdr->shortopts ? strlen(hdr->shortopts) : 0); } result = xmalloc(len); res = result; /* add a leading ':' so getopt return codes distinguish */ /* unrecognised option and options missing argument values */ *res++ = ':'; for (tabindex = 0; !(table[tabindex]->flag & ARG_TERMINATOR); tabindex++) { struct arg_hdr* hdr = table[tabindex]; const char* shortopts = hdr->shortopts; while (shortopts && *shortopts) { *res++ = *shortopts++; if (hdr->flag & ARG_HASVALUE) *res++ = ':'; if (hdr->flag & ARG_HASOPTVALUE) *res++ = ':'; } } /* null terminate the string */ *res = 0; /*printf("alloc_shortoptions() returns \"%s\"\n",(result?result:"NULL"));*/ return result; } /* return index of the table terminator entry */ static int arg_endindex(struct arg_hdr** table) { int tabindex = 0; while (!(table[tabindex]->flag & ARG_TERMINATOR)) tabindex++; return tabindex; } static void arg_parse_tagged(int argc, char** argv, struct arg_hdr** table, struct arg_end* endtable) { struct longoptions* longoptions; char* shortoptions; int copt; /*printf("arg_parse_tagged(%d,%p,%p,%p)\n",argc,argv,table,endtable);*/ /* allocate short and long option arrays for the given opttable[]. */ /* if the allocs fail then put an error msg in the last table entry. */ longoptions = alloc_longoptions(table); shortoptions = alloc_shortoptions(table); /*dump_longoptions(longoptions);*/ /* reset getopts internal option-index to zero, and disable error reporting */ optind = 0; opterr = 0; /* fetch and process args using getopt_long */ #ifdef ARG_LONG_ONLY while ((copt = getopt_long_only(argc, argv, shortoptions, longoptions->options, NULL)) != -1) { #else while ((copt = getopt_long(argc, argv, shortoptions, longoptions->options, NULL)) != -1) { #endif /* printf("optarg='%s'\n",optarg); printf("optind=%d\n",optind); printf("copt=%c\n",(char)copt); printf("optopt=%c (%d)\n",optopt, (int)(optopt)); */ switch (copt) { case 0: { int tabindex = longoptions->getoptval; void* parent = table[tabindex]->parent; /*printf("long option detected from argtable[%d]\n", tabindex);*/ if (optarg && optarg[0] == 0 && (table[tabindex]->flag & ARG_HASVALUE)) { /* printf(": long option %s requires an argument\n",argv[optind-1]); */ arg_register_error(endtable, endtable, ARG_EMISSARG, argv[optind - 1]); /* continue to scan the (empty) argument value to enforce argument count checking */ } if (table[tabindex]->scanfn) { int errorcode = table[tabindex]->scanfn(parent, optarg); if (errorcode != 0) arg_register_error(endtable, parent, errorcode, optarg); } } break; case '?': /* * getopt_long() found an unrecognised short option. * if it was a short option its value is in optopt * if it was a long option then optopt=0 */ switch (optopt) { case 0: /*printf("?0 unrecognised long option %s\n",argv[optind-1]);*/ arg_register_error(endtable, endtable, ARG_ELONGOPT, argv[optind - 1]); break; default: /*printf("?* unrecognised short option '%c'\n",optopt);*/ arg_register_error(endtable, endtable, optopt, NULL); break; } break; case ':': /* * getopt_long() found an option with its argument missing. */ /*printf(": option %s requires an argument\n",argv[optind-1]); */ arg_register_error(endtable, endtable, ARG_EMISSARG, argv[optind - 1]); break; default: { /* getopt_long() found a valid short option */ int tabindex = find_shortoption(table, (char)copt); /*printf("short option detected from argtable[%d]\n", tabindex);*/ if (tabindex == -1) { /* should never get here - but handle it just in case */ /*printf("unrecognised short option %d\n",copt);*/ arg_register_error(endtable, endtable, copt, NULL); } else { if (table[tabindex]->scanfn) { void* parent = table[tabindex]->parent; int errorcode = table[tabindex]->scanfn(parent, optarg); if (errorcode != 0) arg_register_error(endtable, parent, errorcode, optarg); } } break; } } } xfree(shortoptions); xfree(longoptions); } static void arg_parse_untagged(int argc, char** argv, struct arg_hdr** table, struct arg_end* endtable) { int tabindex = 0; int errorlast = 0; const char* optarglast = NULL; void* parentlast = NULL; /*printf("arg_parse_untagged(%d,%p,%p,%p)\n",argc,argv,table,endtable);*/ while (!(table[tabindex]->flag & ARG_TERMINATOR)) { void* parent; int errorcode; /* if we have exhausted our argv[optind] entries then we have finished */ if (optind >= argc) { /*printf("arg_parse_untagged(): argv[] exhausted\n");*/ return; } /* skip table entries with non-null long or short options (they are not untagged entries) */ if (table[tabindex]->longopts || table[tabindex]->shortopts) { /*printf("arg_parse_untagged(): skipping argtable[%d] (tagged argument)\n",tabindex);*/ tabindex++; continue; } /* skip table entries with NULL scanfn */ if (!(table[tabindex]->scanfn)) { /*printf("arg_parse_untagged(): skipping argtable[%d] (NULL scanfn)\n",tabindex);*/ tabindex++; continue; } /* attempt to scan the current argv[optind] with the current */ /* table[tabindex] entry. If it succeeds then keep it, otherwise */ /* try again with the next table[] entry. */ parent = table[tabindex]->parent; errorcode = table[tabindex]->scanfn(parent, argv[optind]); if (errorcode == 0) { /* success, move onto next argv[optind] but stay with same table[tabindex] */ /*printf("arg_parse_untagged(): argtable[%d] successfully matched\n",tabindex);*/ optind++; /* clear the last tentative error */ errorlast = 0; } else { /* failure, try same argv[optind] with next table[tabindex] entry */ /*printf("arg_parse_untagged(): argtable[%d] failed match\n",tabindex);*/ tabindex++; /* remember this as a tentative error we may wish to reinstate later */ errorlast = errorcode; optarglast = argv[optind]; parentlast = parent; } } /* if a tentative error still remains at this point then register it as a proper error */ if (errorlast) { arg_register_error(endtable, parentlast, errorlast, optarglast); optind++; } /* only get here when not all argv[] entries were consumed */ /* register an error for each unused argv[] entry */ while (optind < argc) { /*printf("arg_parse_untagged(): argv[%d]=\"%s\" not consumed\n",optind,argv[optind]);*/ arg_register_error(endtable, endtable, ARG_ENOMATCH, argv[optind++]); } return; } static void arg_parse_check(struct arg_hdr** table, struct arg_end* endtable) { int tabindex = 0; /* printf("arg_parse_check()\n"); */ do { if (table[tabindex]->checkfn) { void* parent = table[tabindex]->parent; int errorcode = table[tabindex]->checkfn(parent); if (errorcode != 0) arg_register_error(endtable, parent, errorcode, NULL); } } while (!(table[tabindex++]->flag & ARG_TERMINATOR)); } static void arg_reset(void** argtable) { struct arg_hdr** table = (struct arg_hdr**)argtable; int tabindex = 0; /*printf("arg_reset(%p)\n",argtable);*/ do { if (table[tabindex]->resetfn) table[tabindex]->resetfn(table[tabindex]->parent); } while (!(table[tabindex++]->flag & ARG_TERMINATOR)); } int arg_parse(int argc, char** argv, void** argtable) { struct arg_hdr** table = (struct arg_hdr**)argtable; struct arg_end* endtable; int endindex; char** argvcopy = NULL; int i; /*printf("arg_parse(%d,%p,%p)\n",argc,argv,argtable);*/ /* reset any argtable data from previous invocations */ arg_reset(argtable); /* locate the first end-of-table marker within the array */ endindex = arg_endindex(table); endtable = (struct arg_end*)table[endindex]; /* Special case of argc==0. This can occur on Texas Instruments DSP. */ /* Failure to trap this case results in an unwanted NULL result from */ /* the malloc for argvcopy (next code block). */ if (argc == 0) { /* We must still perform post-parse checks despite the absence of command line arguments */ arg_parse_check(table, endtable); /* Now we are finished */ return endtable->count; } argvcopy = (char**)xmalloc(sizeof(char*) * (argc + 1)); /* Fill in the local copy of argv[]. We need a local copy because getopt rearranges argv[] which adversely affects subsequent parsing attempts. */ for (i = 0; i < argc; i++) argvcopy[i] = argv[i]; argvcopy[argc] = NULL; /* parse the command line (local copy) for tagged options */ arg_parse_tagged(argc, argvcopy, table, endtable); /* parse the command line (local copy) for untagged options */ arg_parse_untagged(argc, argvcopy, table, endtable); /* if no errors so far then perform post-parse checks otherwise dont bother */ if (endtable->count == 0) arg_parse_check(table, endtable); /* release the local copt of argv[] */ xfree(argvcopy); return endtable->count; } /* * Concatenate contents of src[] string onto *pdest[] string. * The *pdest pointer is altered to point to the end of the * target string and *pndest is decremented by the same number * of chars. * Does not append more than *pndest chars into *pdest[] * so as to prevent buffer overruns. * Its something like strncat() but more efficient for repeated * calls on the same destination string. * Example of use: * char dest[30] = "good" * size_t ndest = sizeof(dest); * char *pdest = dest; * arg_char(&pdest,"bye ",&ndest); * arg_char(&pdest,"cruel ",&ndest); * arg_char(&pdest,"world!",&ndest); * Results in: * dest[] == "goodbye cruel world!" * ndest == 10 */ static void arg_cat(char** pdest, const char* src, size_t* pndest) { char* dest = *pdest; char* end = dest + *pndest; /*locate null terminator of dest string */ while (dest < end && *dest != 0) dest++; /* concat src string to dest string */ while (dest < end && *src != 0) *dest++ = *src++; /* null terminate dest string */ *dest = 0; /* update *pdest and *pndest */ *pndest = end - dest; *pdest = dest; } static void arg_cat_option(char* dest, size_t ndest, const char* shortopts, const char* longopts, const char* datatype, int optvalue) { if (shortopts) { char option[3]; /* note: option array[] is initialized dynamically here to satisfy */ /* a deficiency in the watcom compiler wrt static array initializers. */ option[0] = '-'; option[1] = shortopts[0]; option[2] = 0; arg_cat(&dest, option, &ndest); if (datatype) { arg_cat(&dest, " ", &ndest); if (optvalue) { arg_cat(&dest, "[", &ndest); arg_cat(&dest, datatype, &ndest); arg_cat(&dest, "]", &ndest); } else arg_cat(&dest, datatype, &ndest); } } else if (longopts) { size_t ncspn; /* add "--" tag prefix */ arg_cat(&dest, "--", &ndest); /* add comma separated option tag */ ncspn = strcspn(longopts, ","); #if (defined(__STDC_LIB_EXT1__) && defined(__STDC_WANT_LIB_EXT1__)) || (defined(__STDC_SECURE_LIB__) && defined(__STDC_WANT_SECURE_LIB__)) strncat_s(dest, ndest, longopts, (ncspn < ndest) ? ncspn : ndest); #else strncat(dest, longopts, (ncspn < ndest) ? ncspn : ndest); #endif if (datatype) { arg_cat(&dest, "=", &ndest); if (optvalue) { arg_cat(&dest, "[", &ndest); arg_cat(&dest, datatype, &ndest); arg_cat(&dest, "]", &ndest); } else arg_cat(&dest, datatype, &ndest); } } else if (datatype) { if (optvalue) { arg_cat(&dest, "[", &ndest); arg_cat(&dest, datatype, &ndest); arg_cat(&dest, "]", &ndest); } else arg_cat(&dest, datatype, &ndest); } } static void arg_cat_optionv(char* dest, size_t ndest, const char* shortopts, const char* longopts, const char* datatype, int optvalue, const char* separator) { separator = separator ? separator : ""; if (shortopts) { const char* c = shortopts; while (*c) { /* "-a|-b|-c" */ char shortopt[3]; /* note: shortopt array[] is initialized dynamically here to satisfy */ /* a deficiency in the watcom compiler wrt static array initializers. */ shortopt[0] = '-'; shortopt[1] = *c; shortopt[2] = 0; arg_cat(&dest, shortopt, &ndest); if (*++c) arg_cat(&dest, separator, &ndest); } } /* put separator between long opts and short opts */ if (shortopts && longopts) arg_cat(&dest, separator, &ndest); if (longopts) { const char* c = longopts; while (*c) { size_t ncspn; /* add "--" tag prefix */ arg_cat(&dest, "--", &ndest); /* add comma separated option tag */ ncspn = strcspn(c, ","); #if (defined(__STDC_LIB_EXT1__) && defined(__STDC_WANT_LIB_EXT1__)) || (defined(__STDC_SECURE_LIB__) && defined(__STDC_WANT_SECURE_LIB__)) strncat_s(dest, ndest, c, (ncspn < ndest) ? ncspn : ndest); #else strncat(dest, c, (ncspn < ndest) ? ncspn : ndest); #endif c += ncspn; /* add given separator in place of comma */ if (*c == ',') { arg_cat(&dest, separator, &ndest); c++; } } } if (datatype) { if (longopts) arg_cat(&dest, "=", &ndest); else if (shortopts) arg_cat(&dest, " ", &ndest); if (optvalue) { arg_cat(&dest, "[", &ndest); arg_cat(&dest, datatype, &ndest); arg_cat(&dest, "]", &ndest); } else arg_cat(&dest, datatype, &ndest); } } void arg_print_option_ds(arg_dstr_t ds, const char* shortopts, const char* longopts, const char* datatype, const char* suffix) { char syntax[200] = ""; suffix = suffix ? suffix : ""; /* there is no way of passing the proper optvalue for optional argument values here, so we must ignore it */ arg_cat_optionv(syntax, sizeof(syntax) - 1, shortopts, longopts, datatype, 0, "|"); arg_dstr_cat(ds, syntax); arg_dstr_cat(ds, (char*)suffix); } /* this function should be deprecated because it doesn't consider optional argument values (ARG_HASOPTVALUE) */ void arg_print_option(FILE* fp, const char* shortopts, const char* longopts, const char* datatype, const char* suffix) { arg_dstr_t ds = arg_dstr_create(); arg_print_option_ds(ds, shortopts, longopts, datatype, suffix); fputs(arg_dstr_cstr(ds), fp); arg_dstr_destroy(ds); } /* * Print a GNU style [OPTION] string in which all short options that * do not take argument values are presented in abbreviated form, as * in: -xvfsd, or -xvf[sd], or [-xvsfd] */ static void arg_print_gnuswitch_ds(arg_dstr_t ds, struct arg_hdr** table) { int tabindex; char* format1 = " -%c"; char* format2 = " [-%c"; char* suffix = ""; /* print all mandatory switches that are without argument values */ for (tabindex = 0; table[tabindex] && !(table[tabindex]->flag & ARG_TERMINATOR); tabindex++) { /* skip optional options */ if (table[tabindex]->mincount < 1) continue; /* skip non-short options */ if (table[tabindex]->shortopts == NULL) continue; /* skip options that take argument values */ if (table[tabindex]->flag & ARG_HASVALUE) continue; /* print the short option (only the first short option char, ignore multiple choices)*/ arg_dstr_catf(ds, format1, table[tabindex]->shortopts[0]); format1 = "%c"; format2 = "[%c"; } /* print all optional switches that are without argument values */ for (tabindex = 0; table[tabindex] && !(table[tabindex]->flag & ARG_TERMINATOR); tabindex++) { /* skip mandatory args */ if (table[tabindex]->mincount > 0) continue; /* skip args without short options */ if (table[tabindex]->shortopts == NULL) continue; /* skip args with values */ if (table[tabindex]->flag & ARG_HASVALUE) continue; /* print first short option */ arg_dstr_catf(ds, format2, table[tabindex]->shortopts[0]); format2 = "%c"; suffix = "]"; } arg_dstr_catf(ds, "%s", suffix); } void arg_print_syntax_ds(arg_dstr_t ds, void** argtable, const char* suffix) { struct arg_hdr** table = (struct arg_hdr**)argtable; int i, tabindex; /* print GNU style [OPTION] string */ arg_print_gnuswitch_ds(ds, table); /* print remaining options in abbreviated style */ for (tabindex = 0; table[tabindex] && !(table[tabindex]->flag & ARG_TERMINATOR); tabindex++) { char syntax[200] = ""; const char *shortopts, *longopts, *datatype; /* skip short options without arg values (they were printed by arg_print_gnu_switch) */ if (table[tabindex]->shortopts && !(table[tabindex]->flag & ARG_HASVALUE)) continue; shortopts = table[tabindex]->shortopts; longopts = table[tabindex]->longopts; datatype = table[tabindex]->datatype; arg_cat_option(syntax, sizeof(syntax) - 1, shortopts, longopts, datatype, table[tabindex]->flag & ARG_HASOPTVALUE); if (strlen(syntax) > 0) { /* print mandatory instances of this option */ for (i = 0; i < table[tabindex]->mincount; i++) { arg_dstr_cat(ds, " "); arg_dstr_cat(ds, syntax); } /* print optional instances enclosed in "[..]" */ switch (table[tabindex]->maxcount - table[tabindex]->mincount) { case 0: break; case 1: arg_dstr_cat(ds, " ["); arg_dstr_cat(ds, syntax); arg_dstr_cat(ds, "]"); break; case 2: arg_dstr_cat(ds, " ["); arg_dstr_cat(ds, syntax); arg_dstr_cat(ds, "]"); arg_dstr_cat(ds, " ["); arg_dstr_cat(ds, syntax); arg_dstr_cat(ds, "]"); break; default: arg_dstr_cat(ds, " ["); arg_dstr_cat(ds, syntax); arg_dstr_cat(ds, "]..."); break; } } } if (suffix) { arg_dstr_cat(ds, (char*)suffix); } } void arg_print_syntax(FILE* fp, void** argtable, const char* suffix) { arg_dstr_t ds = arg_dstr_create(); arg_print_syntax_ds(ds, argtable, suffix); fputs(arg_dstr_cstr(ds), fp); arg_dstr_destroy(ds); } void arg_print_syntaxv_ds(arg_dstr_t ds, void** argtable, const char* suffix) { struct arg_hdr** table = (struct arg_hdr**)argtable; int i, tabindex; /* print remaining options in abbreviated style */ for (tabindex = 0; table[tabindex] && !(table[tabindex]->flag & ARG_TERMINATOR); tabindex++) { char syntax[200] = ""; const char *shortopts, *longopts, *datatype; shortopts = table[tabindex]->shortopts; longopts = table[tabindex]->longopts; datatype = table[tabindex]->datatype; arg_cat_optionv(syntax, sizeof(syntax) - 1, shortopts, longopts, datatype, table[tabindex]->flag & ARG_HASOPTVALUE, "|"); /* print mandatory options */ for (i = 0; i < table[tabindex]->mincount; i++) { arg_dstr_cat(ds, " "); arg_dstr_cat(ds, syntax); } /* print optional args enclosed in "[..]" */ switch (table[tabindex]->maxcount - table[tabindex]->mincount) { case 0: break; case 1: arg_dstr_cat(ds, " ["); arg_dstr_cat(ds, syntax); arg_dstr_cat(ds, "]"); break; case 2: arg_dstr_cat(ds, " ["); arg_dstr_cat(ds, syntax); arg_dstr_cat(ds, "]"); arg_dstr_cat(ds, " ["); arg_dstr_cat(ds, syntax); arg_dstr_cat(ds, "]"); break; default: arg_dstr_cat(ds, " ["); arg_dstr_cat(ds, syntax); arg_dstr_cat(ds, "]..."); break; } } if (suffix) { arg_dstr_cat(ds, (char*)suffix); } } void arg_print_syntaxv(FILE* fp, void** argtable, const char* suffix) { arg_dstr_t ds = arg_dstr_create(); arg_print_syntaxv_ds(ds, argtable, suffix); fputs(arg_dstr_cstr(ds), fp); arg_dstr_destroy(ds); } void arg_print_glossary_ds(arg_dstr_t ds, void** argtable, const char* format) { struct arg_hdr** table = (struct arg_hdr**)argtable; int tabindex; format = format ? format : " %-20s %s\n"; for (tabindex = 0; !(table[tabindex]->flag & ARG_TERMINATOR); tabindex++) { if (table[tabindex]->glossary) { char syntax[200] = ""; const char* shortopts = table[tabindex]->shortopts; const char* longopts = table[tabindex]->longopts; const char* datatype = table[tabindex]->datatype; const char* glossary = table[tabindex]->glossary; arg_cat_optionv(syntax, sizeof(syntax) - 1, shortopts, longopts, datatype, table[tabindex]->flag & ARG_HASOPTVALUE, ", "); arg_dstr_catf(ds, format, syntax, glossary); } } } void arg_print_glossary(FILE* fp, void** argtable, const char* format) { arg_dstr_t ds = arg_dstr_create(); arg_print_glossary_ds(ds, argtable, format); fputs(arg_dstr_cstr(ds), fp); arg_dstr_destroy(ds); } /** * Print a piece of text formatted, which means in a column with a * left and a right margin. The lines are wrapped at whitspaces next * to right margin. The function does not indent the first line, but * only the following ones. * * Example: * arg_print_formatted( fp, 0, 5, "Some text that doesn't fit." ) * will result in the following output: * * Some * text * that * doesn' * t fit. * * Too long lines will be wrapped in the middle of a word. * * arg_print_formatted( fp, 2, 7, "Some text that doesn't fit." ) * will result in the following output: * * Some * text * that * doesn' * t fit. * * As you see, the first line is not indented. This enables output of * lines, which start in a line where output already happened. * * Author: Uli Fouquet */ static void arg_print_formatted_ds(arg_dstr_t ds, const unsigned lmargin, const unsigned rmargin, const char* text) { const unsigned int textlen = (unsigned int)strlen(text); unsigned int line_start = 0; unsigned int line_end = textlen; const unsigned int colwidth = (rmargin - lmargin) + 1; assert(strlen(text) < UINT_MAX); /* Someone doesn't like us... */ if (line_end < line_start) { arg_dstr_catf(ds, "%s\n", text); } while (line_end > line_start) { /* Eat leading white spaces. This is essential because while wrapping lines, there will often be a whitespace at beginning of line */ while (isspace((int)(*(text + line_start)))) { line_start++; } /* Find last whitespace, that fits into line */ if (line_end - line_start > colwidth) { line_end = line_start + colwidth; while ((line_end > line_start) && !isspace((int)(*(text + line_end)))) { line_end--; } /* Consume trailing spaces */ while ((line_end > line_start) && isspace((int)(*(text + line_end)))) { line_end--; } /* Restore the last non-space character */ line_end++; } /* Output line of text */ while (line_start < line_end) { char c = *(text + line_start); arg_dstr_catc(ds, c); line_start++; } arg_dstr_cat(ds, "\n"); /* Initialize another line */ if (line_end < textlen) { unsigned i; for (i = 0; i < lmargin; i++) { arg_dstr_cat(ds, " "); } line_end = textlen; } } /* lines of text */ } /** * Prints the glossary in strict GNU format. * Differences to arg_print_glossary() are: * - wraps lines after 80 chars * - indents lines without shortopts * - does not accept formatstrings * * Contributed by Uli Fouquet */ void arg_print_glossary_gnu_ds(arg_dstr_t ds, void** argtable) { struct arg_hdr** table = (struct arg_hdr**)argtable; int tabindex; for (tabindex = 0; !(table[tabindex]->flag & ARG_TERMINATOR); tabindex++) { if (table[tabindex]->glossary) { char syntax[200] = ""; const char* shortopts = table[tabindex]->shortopts; const char* longopts = table[tabindex]->longopts; const char* datatype = table[tabindex]->datatype; const char* glossary = table[tabindex]->glossary; if (!shortopts && longopts) { /* Indent trailing line by 4 spaces... */ memset(syntax, ' ', 4); *(syntax + 4) = '\0'; } arg_cat_optionv(syntax, sizeof(syntax) - 1, shortopts, longopts, datatype, table[tabindex]->flag & ARG_HASOPTVALUE, ", "); /* If syntax fits not into column, print glossary in new line... */ if (strlen(syntax) > 25) { arg_dstr_catf(ds, " %-25s %s\n", syntax, ""); *syntax = '\0'; } arg_dstr_catf(ds, " %-25s ", syntax); arg_print_formatted_ds(ds, 28, 79, glossary); } } /* for each table entry */ arg_dstr_cat(ds, "\n"); } void arg_print_glossary_gnu(FILE* fp, void** argtable) { arg_dstr_t ds = arg_dstr_create(); arg_print_glossary_gnu_ds(ds, argtable); fputs(arg_dstr_cstr(ds), fp); arg_dstr_destroy(ds); } /** * Checks the argtable[] array for NULL entries and returns 1 * if any are found, zero otherwise. */ int arg_nullcheck(void** argtable) { struct arg_hdr** table = (struct arg_hdr**)argtable; int tabindex; /*printf("arg_nullcheck(%p)\n",argtable);*/ if (!table) return 1; tabindex = 0; do { /*printf("argtable[%d]=%p\n",tabindex,argtable[tabindex]);*/ if (!table[tabindex]) return 1; } while (!(table[tabindex++]->flag & ARG_TERMINATOR)); return 0; } /* * arg_free() is deprecated in favour of arg_freetable() due to a flaw in its design. * The flaw results in memory leak in the (very rare) case that an intermediate * entry in the argtable array failed its memory allocation while others following * that entry were still allocated ok. Those subsequent allocations will not be * deallocated by arg_free(). * Despite the unlikeliness of the problem occurring, and the even unlikelier event * that it has any deleterious effect, it is fixed regardless by replacing arg_free() * with the newer arg_freetable() function. * We still keep arg_free() for backwards compatibility. */ void arg_free(void** argtable) { struct arg_hdr** table = (struct arg_hdr**)argtable; int tabindex = 0; int flag; /*printf("arg_free(%p)\n",argtable);*/ do { /* if we encounter a NULL entry then somewhat incorrectly we presume we have come to the end of the array. It isnt strictly true because an intermediate entry could be NULL with other non-NULL entries to follow. The subsequent argtable entries would then not be freed as they should. */ if (table[tabindex] == NULL) break; flag = table[tabindex]->flag; xfree(table[tabindex]); table[tabindex++] = NULL; } while (!(flag & ARG_TERMINATOR)); } /* frees each non-NULL element of argtable[], where n is the size of the number of entries in the array */ void arg_freetable(void** argtable, size_t n) { struct arg_hdr** table = (struct arg_hdr**)argtable; size_t tabindex = 0; /*printf("arg_freetable(%p)\n",argtable);*/ for (tabindex = 0; tabindex < n; tabindex++) { if (table[tabindex] == NULL) continue; xfree(table[tabindex]); table[tabindex] = NULL; }; } #ifdef _WIN32 BOOL WINAPI DllMain(HINSTANCE hinstDLL, DWORD fdwReason, LPVOID lpvReserved) { return TRUE; UNREFERENCED_PARAMETER(hinstDLL); UNREFERENCED_PARAMETER(fdwReason); UNREFERENCED_PARAMETER(lpvReserved); } #endif sslh-2.1.4/argtable3.h000066400000000000000000000343201463704647400145250ustar00rootroot00000000000000/******************************************************************************* * argtable3: Declares the main interfaces of the library * * This file is part of the argtable3 library. * * Copyright (C) 1998-2001,2003-2011,2013 Stewart Heitmann * * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * * Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * Neither the name of STEWART HEITMANN nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL STEWART HEITMANN BE LIABLE FOR ANY DIRECT, * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ******************************************************************************/ #ifndef ARGTABLE3 #define ARGTABLE3 #include /* FILE */ #include /* struct tm */ #ifdef __cplusplus extern "C" { #endif #define ARG_REX_ICASE 1 #define ARG_DSTR_SIZE 200 #define ARG_CMD_NAME_LEN 100 #define ARG_CMD_DESCRIPTION_LEN 256 #ifndef ARG_REPLACE_GETOPT #define ARG_REPLACE_GETOPT 1 /* use the embedded getopt as the system getopt(3) */ #endif /* ARG_REPLACE_GETOPT */ /* bit masks for arg_hdr.flag */ enum { ARG_TERMINATOR = 0x1, ARG_HASVALUE = 0x2, ARG_HASOPTVALUE = 0x4 }; #if defined(_WIN32) #if defined(argtable3_EXPORTS) #define ARG_EXTERN __declspec(dllexport) #elif defined(argtable3_IMPORTS) #define ARG_EXTERN __declspec(dllimport) #else #define ARG_EXTERN #endif #else #define ARG_EXTERN #endif typedef struct _internal_arg_dstr* arg_dstr_t; typedef void* arg_cmd_itr_t; typedef void(arg_resetfn)(void* parent); typedef int(arg_scanfn)(void* parent, const char* argval); typedef int(arg_checkfn)(void* parent); typedef void(arg_errorfn)(void* parent, arg_dstr_t ds, int error, const char* argval, const char* progname); typedef void(arg_dstr_freefn)(char* buf); typedef int(arg_cmdfn)(int argc, char* argv[], arg_dstr_t res); typedef int(arg_comparefn)(const void* k1, const void* k2); /* * The arg_hdr struct defines properties that are common to all arg_xxx structs. * The argtable library requires each arg_xxx struct to have an arg_hdr * struct as its first data member. * The argtable library functions then use this data to identify the * properties of the command line option, such as its option tags, * datatype string, and glossary strings, and so on. * Moreover, the arg_hdr struct contains pointers to custom functions that * are provided by each arg_xxx struct which perform the tasks of parsing * that particular arg_xxx arguments, performing post-parse checks, and * reporting errors. * These functions are private to the individual arg_xxx source code * and are the pointer to them are initialised by that arg_xxx struct's * constructor function. The user could alter them after construction * if desired, but the original intention is for them to be set by the * constructor and left unaltered. */ typedef struct arg_hdr { char flag; /* Modifier flags: ARG_TERMINATOR, ARG_HASVALUE. */ const char* shortopts; /* String defining the short options */ const char* longopts; /* String defining the long options */ const char* datatype; /* Description of the argument data type */ const char* glossary; /* Description of the option as shown by arg_print_glossary function */ int mincount; /* Minimum number of occurences of this option accepted */ int maxcount; /* Maximum number of occurences if this option accepted */ void* parent; /* Pointer to parent arg_xxx struct */ arg_resetfn* resetfn; /* Pointer to parent arg_xxx reset function */ arg_scanfn* scanfn; /* Pointer to parent arg_xxx scan function */ arg_checkfn* checkfn; /* Pointer to parent arg_xxx check function */ arg_errorfn* errorfn; /* Pointer to parent arg_xxx error function */ void* priv; /* Pointer to private header data for use by arg_xxx functions */ } arg_hdr_t; typedef struct arg_rem { struct arg_hdr hdr; /* The mandatory argtable header struct */ } arg_rem_t; typedef struct arg_lit { struct arg_hdr hdr; /* The mandatory argtable header struct */ int count; /* Number of matching command line args */ } arg_lit_t; typedef struct arg_int { struct arg_hdr hdr; /* The mandatory argtable header struct */ int count; /* Number of matching command line args */ int* ival; /* Array of parsed argument values */ } arg_int_t; typedef struct arg_dbl { struct arg_hdr hdr; /* The mandatory argtable header struct */ int count; /* Number of matching command line args */ double* dval; /* Array of parsed argument values */ } arg_dbl_t; typedef struct arg_str { struct arg_hdr hdr; /* The mandatory argtable header struct */ int count; /* Number of matching command line args */ const char** sval; /* Array of parsed argument values */ } arg_str_t; typedef struct arg_rex { struct arg_hdr hdr; /* The mandatory argtable header struct */ int count; /* Number of matching command line args */ const char** sval; /* Array of parsed argument values */ } arg_rex_t; typedef struct arg_file { struct arg_hdr hdr; /* The mandatory argtable header struct */ int count; /* Number of matching command line args*/ const char** filename; /* Array of parsed filenames (eg: /home/foo.bar) */ const char** basename; /* Array of parsed basenames (eg: foo.bar) */ const char** extension; /* Array of parsed extensions (eg: .bar) */ } arg_file_t; typedef struct arg_date { struct arg_hdr hdr; /* The mandatory argtable header struct */ const char* format; /* strptime format string used to parse the date */ int count; /* Number of matching command line args */ struct tm* tmval; /* Array of parsed time values */ } arg_date_t; enum { ARG_ELIMIT = 1, ARG_EMALLOC, ARG_ENOMATCH, ARG_ELONGOPT, ARG_EMISSARG }; typedef struct arg_end { struct arg_hdr hdr; /* The mandatory argtable header struct */ int count; /* Number of errors encountered */ int* error; /* Array of error codes */ void** parent; /* Array of pointers to offending arg_xxx struct */ const char** argval; /* Array of pointers to offending argv[] string */ } arg_end_t; typedef struct arg_cmd_info { char name[ARG_CMD_NAME_LEN]; char description[ARG_CMD_DESCRIPTION_LEN]; arg_cmdfn* proc; } arg_cmd_info_t; /**** arg_xxx constructor functions *********************************/ ARG_EXTERN struct arg_rem* arg_rem(const char* datatype, const char* glossary); ARG_EXTERN struct arg_lit* arg_lit0(const char* shortopts, const char* longopts, const char* glossary); ARG_EXTERN struct arg_lit* arg_lit1(const char* shortopts, const char* longopts, const char* glossary); ARG_EXTERN struct arg_lit* arg_litn(const char* shortopts, const char* longopts, int mincount, int maxcount, const char* glossary); ARG_EXTERN struct arg_int* arg_int0(const char* shortopts, const char* longopts, const char* datatype, const char* glossary); ARG_EXTERN struct arg_int* arg_int1(const char* shortopts, const char* longopts, const char* datatype, const char* glossary); ARG_EXTERN struct arg_int* arg_intn(const char* shortopts, const char* longopts, const char* datatype, int mincount, int maxcount, const char* glossary); ARG_EXTERN struct arg_dbl* arg_dbl0(const char* shortopts, const char* longopts, const char* datatype, const char* glossary); ARG_EXTERN struct arg_dbl* arg_dbl1(const char* shortopts, const char* longopts, const char* datatype, const char* glossary); ARG_EXTERN struct arg_dbl* arg_dbln(const char* shortopts, const char* longopts, const char* datatype, int mincount, int maxcount, const char* glossary); ARG_EXTERN struct arg_str* arg_str0(const char* shortopts, const char* longopts, const char* datatype, const char* glossary); ARG_EXTERN struct arg_str* arg_str1(const char* shortopts, const char* longopts, const char* datatype, const char* glossary); ARG_EXTERN struct arg_str* arg_strn(const char* shortopts, const char* longopts, const char* datatype, int mincount, int maxcount, const char* glossary); ARG_EXTERN struct arg_rex* arg_rex0(const char* shortopts, const char* longopts, const char* pattern, const char* datatype, int flags, const char* glossary); ARG_EXTERN struct arg_rex* arg_rex1(const char* shortopts, const char* longopts, const char* pattern, const char* datatype, int flags, const char* glossary); ARG_EXTERN struct arg_rex* arg_rexn(const char* shortopts, const char* longopts, const char* pattern, const char* datatype, int mincount, int maxcount, int flags, const char* glossary); ARG_EXTERN struct arg_file* arg_file0(const char* shortopts, const char* longopts, const char* datatype, const char* glossary); ARG_EXTERN struct arg_file* arg_file1(const char* shortopts, const char* longopts, const char* datatype, const char* glossary); ARG_EXTERN struct arg_file* arg_filen(const char* shortopts, const char* longopts, const char* datatype, int mincount, int maxcount, const char* glossary); ARG_EXTERN struct arg_date* arg_date0(const char* shortopts, const char* longopts, const char* format, const char* datatype, const char* glossary); ARG_EXTERN struct arg_date* arg_date1(const char* shortopts, const char* longopts, const char* format, const char* datatype, const char* glossary); ARG_EXTERN struct arg_date* arg_daten(const char* shortopts, const char* longopts, const char* format, const char* datatype, int mincount, int maxcount, const char* glossary); ARG_EXTERN struct arg_end* arg_end(int maxerrors); #define ARG_DSTR_STATIC ((arg_dstr_freefn*)0) #define ARG_DSTR_VOLATILE ((arg_dstr_freefn*)1) #define ARG_DSTR_DYNAMIC ((arg_dstr_freefn*)3) /**** other functions *******************************************/ ARG_EXTERN int arg_nullcheck(void** argtable); ARG_EXTERN int arg_parse(int argc, char** argv, void** argtable); ARG_EXTERN void arg_print_option(FILE* fp, const char* shortopts, const char* longopts, const char* datatype, const char* suffix); ARG_EXTERN void arg_print_syntax(FILE* fp, void** argtable, const char* suffix); ARG_EXTERN void arg_print_syntaxv(FILE* fp, void** argtable, const char* suffix); ARG_EXTERN void arg_print_glossary(FILE* fp, void** argtable, const char* format); ARG_EXTERN void arg_print_glossary_gnu(FILE* fp, void** argtable); ARG_EXTERN void arg_print_errors(FILE* fp, struct arg_end* end, const char* progname); ARG_EXTERN void arg_print_option_ds(arg_dstr_t ds, const char* shortopts, const char* longopts, const char* datatype, const char* suffix); ARG_EXTERN void arg_print_syntax_ds(arg_dstr_t ds, void** argtable, const char* suffix); ARG_EXTERN void arg_print_syntaxv_ds(arg_dstr_t ds, void** argtable, const char* suffix); ARG_EXTERN void arg_print_glossary_ds(arg_dstr_t ds, void** argtable, const char* format); ARG_EXTERN void arg_print_glossary_gnu_ds(arg_dstr_t ds, void** argtable); ARG_EXTERN void arg_print_errors_ds(arg_dstr_t ds, struct arg_end* end, const char* progname); ARG_EXTERN void arg_freetable(void** argtable, size_t n); ARG_EXTERN arg_dstr_t arg_dstr_create(void); ARG_EXTERN void arg_dstr_destroy(arg_dstr_t ds); ARG_EXTERN void arg_dstr_reset(arg_dstr_t ds); ARG_EXTERN void arg_dstr_free(arg_dstr_t ds); ARG_EXTERN void arg_dstr_set(arg_dstr_t ds, char* str, arg_dstr_freefn* free_proc); ARG_EXTERN void arg_dstr_cat(arg_dstr_t ds, const char* str); ARG_EXTERN void arg_dstr_catc(arg_dstr_t ds, char c); ARG_EXTERN void arg_dstr_catf(arg_dstr_t ds, const char* fmt, ...); ARG_EXTERN char* arg_dstr_cstr(arg_dstr_t ds); ARG_EXTERN void arg_cmd_init(void); ARG_EXTERN void arg_cmd_uninit(void); ARG_EXTERN void arg_cmd_register(const char* name, arg_cmdfn* proc, const char* description); ARG_EXTERN void arg_cmd_unregister(const char* name); ARG_EXTERN int arg_cmd_dispatch(const char* name, int argc, char* argv[], arg_dstr_t res); ARG_EXTERN unsigned int arg_cmd_count(void); ARG_EXTERN arg_cmd_info_t* arg_cmd_info(const char* name); ARG_EXTERN arg_cmd_itr_t arg_cmd_itr_create(void); ARG_EXTERN void arg_cmd_itr_destroy(arg_cmd_itr_t itr); ARG_EXTERN int arg_cmd_itr_advance(arg_cmd_itr_t itr); ARG_EXTERN char* arg_cmd_itr_key(arg_cmd_itr_t itr); ARG_EXTERN arg_cmd_info_t* arg_cmd_itr_value(arg_cmd_itr_t itr); ARG_EXTERN int arg_cmd_itr_search(arg_cmd_itr_t itr, void* k); ARG_EXTERN void arg_mgsort(void* data, int size, int esize, int i, int k, arg_comparefn* comparefn); ARG_EXTERN void arg_make_get_help_msg(arg_dstr_t res); ARG_EXTERN void arg_make_help_msg(arg_dstr_t ds, char* cmd_name, void** argtable); ARG_EXTERN void arg_make_syntax_err_msg(arg_dstr_t ds, void** argtable, struct arg_end* end); ARG_EXTERN int arg_make_syntax_err_help_msg(arg_dstr_t ds, char* name, int help, int nerrors, void** argtable, struct arg_end* end, int* exitcode); ARG_EXTERN void arg_set_module_name(const char* name); ARG_EXTERN void arg_set_module_version(int major, int minor, int patch, const char* tag); /**** deprecated functions, for back-compatibility only ********/ ARG_EXTERN void arg_free(void** argtable); #ifdef __cplusplus } #endif #endif sslh-2.1.4/basic.cfg000066400000000000000000000017131463704647400142520ustar00rootroot00000000000000# This is a basic configuration file that should provide # sensible values for "standard" setup. # You will find extensive examples with explanations in # example.cfg timeout: 2; user: "nobody"; pidfile: "/var/run/sslh.pid"; # Change hostname with your external address name, or the IP # of the interface that receives connections # Default is to bind all interfaces. httpd can be started # first to bind on localhost, in which case sslh will bind # only other interfaces. listen: ( { host: "0.0.0.0"; port: "443"; }, { host: "[::]"; port: "443"; } ); # Change to the protocols you want to forward to. The # defaults here are sensible for services running on # localhost protocols: ( { name: "ssh"; service: "ssh"; host: "localhost"; port: "22"; fork: true; }, { name: "openvpn"; host: "localhost"; port: "1194"; }, { name: "tls"; host: "localhost"; port: "443"; log_level: 0; }, { name: "anyprot"; host: "localhost"; port: "443"; } ); sslh-2.1.4/collection.c000066400000000000000000000053261463704647400150130ustar00rootroot00000000000000/* collection.c: management of a collection of connections, for sslh-select # Copyright (C) 2021 Yves Rutschle # # 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. # # The full text for the General Public License is here: # http://www.gnu.org/licenses/gpl.html */ #include "common.h" #include "collection.h" #include "sslh-conf.h" #include "gap.h" /* Info to keep track of all connections */ struct cnx_collection { gap_array* fd2cnx; /* Array indexed by file descriptor to things in cnx[] */ }; /* Allocates and initialises a new collection of connections with at least * `len` elements. */ cnx_collection* collection_init(int len) { cnx_collection* collection; collection = malloc(sizeof(*collection)); CHECK_ALLOC(collection, "collection_init(collection)"); memset(collection, 0, sizeof(*collection)); collection->fd2cnx = gap_init(len); return collection; } /* Caveat: might not work, as has never been used */ void collection_destroy(cnx_collection* collection) { /* Caveat 2: no code to free connections yet */ gap_destroy(collection->fd2cnx); free(collection); } /* Points the file descriptor to the specified connection index */ int collection_add_fd(cnx_collection* collection, struct connection* cnx, int fd) { gap_set(collection->fd2cnx, fd, cnx); return 0; } /* Allocates a connection and inits it with specified file descriptor */ struct connection* collection_alloc_cnx_from_fd(struct cnx_collection* collection, int fd) { struct connection* cnx = malloc(sizeof(*cnx)); if (!cnx) return NULL; init_cnx(cnx); cnx->type = SOCK_STREAM; cnx->q[0].fd = fd; cnx->state = ST_PROBING; cnx->probe_timeout = time(NULL) + cfg.timeout; gap_set(collection->fd2cnx, fd, cnx); return cnx; } /* Remove a connection from the collection */ int collection_remove_cnx(cnx_collection* collection, struct connection *cnx) { if (cnx->q[0].fd != -1) gap_set(collection->fd2cnx, cnx->q[0].fd, NULL); if (cnx->q[1].fd != -1) gap_set(collection->fd2cnx, cnx->q[1].fd, NULL); free(cnx); return 0; } /* Returns the connection that contains the file descriptor */ struct connection* collection_get_cnx_from_fd(struct cnx_collection* collection, int fd) { return gap_get(collection->fd2cnx, fd); } sslh-2.1.4/collection.h000066400000000000000000000011041463704647400150060ustar00rootroot00000000000000#ifndef COLLECTION_H #define COLLECTION_H typedef struct cnx_collection cnx_collection; cnx_collection* collection_init(int len); void collection_destroy(cnx_collection* collection); struct connection* collection_alloc_cnx_from_fd(cnx_collection* collection, int fd); int collection_add_fd(cnx_collection* collection, struct connection* cnx, int fd); /* Remove a connection from the collection */ int collection_remove_cnx(cnx_collection* collection, struct connection *cnx); struct connection* collection_get_cnx_from_fd(struct cnx_collection* collection, int fd); #endif sslh-2.1.4/common.c000066400000000000000000000621661463704647400141550ustar00rootroot00000000000000/* Code and variables that is common to both fork and select-based * servers. * * No code here should assume whether sockets are blocking or not. **/ #define _GNU_SOURCE #include #include #include #include #include #include #include "common.h" #include "probe.h" #include "log.h" #include "sslh-conf.h" /* Added to make the code compilable under CYGWIN * */ #ifndef SA_NOCLDWAIT #define SA_NOCLDWAIT 0 #endif /* Make use of systemd socket activation * */ #ifdef SYSTEMD #include #endif #ifdef LIBBSD #include #endif /* * Settings that depend on the command line or the config file */ struct sslhcfg_item cfg; struct addrinfo *addr_listen = NULL; /* what addresses do we listen to? */ #ifdef LIBWRAP #include int allow_severity =0, deny_severity = 0; #endif typedef enum { CR_DIE, CR_WARN } CR_ACTION; /* check result and die, printing the offending address and error */ void check_res_dump(CR_ACTION act, int res, struct addrinfo *addr, char* syscall) { char buf[NI_MAXHOST]; if (res == -1) { print_message(msg_system_error, "%s:%s: %s\n", sprintaddr(buf, sizeof(buf), addr), syscall, strerror(errno)); if (act == CR_DIE) exit(1); } } int get_fd_sockets(struct listen_endpoint *sockfd[]) { int sd = 0; #ifdef SYSTEMD sd = sd_listen_fds(0); if (sd < 0) { print_message(msg_system_error, "sd_listen_fds(): %s\n", strerror(-sd)); exit(1); } if (sd > 0) { int i; *sockfd = malloc(sd * sizeof(*sockfd[0])); CHECK_ALLOC(*sockfd, "malloc"); for (i = 0; i < sd; i++) { (*sockfd)[i].socketfd = SD_LISTEN_FDS_START + i; (*sockfd)[i].type = SOCK_STREAM; } } #endif return sd; } /* Set TCP_FASTOPEN on listening socket if all client protocols support it */ int make_listen_tfo(int s) { int i, qlen = 5; /* Don't do it if not supported */ if (!TCP_FASTOPEN) return 0; /* Don't do it if any protocol does not specify it */ for (i = 0; i < cfg.protocols_len; i++) { if (! cfg.protocols[i].tfo_ok) return 0; } return setsockopt(s, SOL_SOCKET, TCP_FASTOPEN, (char*)&qlen, sizeof(qlen)); } /* Starts listening on a single address * Returns a socket filehandle, or dies with message in case of major error */ int listen_single_addr(struct addrinfo* addr, int keepalive, int udp) { struct sockaddr_storage *saddr; int sockfd, one, res; saddr = (struct sockaddr_storage*)addr->ai_addr; sockfd = socket(saddr->ss_family, udp ? SOCK_DGRAM : SOCK_STREAM, 0); check_res_dump(CR_DIE, sockfd, addr, "socket"); one = 1; res = setsockopt(sockfd, SOL_SOCKET, SO_REUSEADDR, (char*)&one, sizeof(one)); check_res_dump(CR_DIE, res, addr, "setsockopt(SO_REUSEADDR)"); res = make_listen_tfo(sockfd); check_res_dump(CR_WARN, res, addr, "setsockopt(TCP_FASTOPEN)"); if (keepalive) { res = setsockopt(sockfd, SOL_SOCKET, SO_KEEPALIVE, (char*)&one, sizeof(one)); check_res_dump(CR_DIE, res, addr, "setsockopt(SO_KEEPALIVE)"); } if (IP_FREEBIND) { res = setsockopt(sockfd, IPPROTO_IP, IP_FREEBIND, (char*)&one, sizeof(one)); check_res_dump(CR_WARN, res, addr, "setsockopt(IP_FREEBIND)"); } if (addr->ai_addr->sa_family == AF_INET6) { res = setsockopt(sockfd, IPPROTO_IPV6, IPV6_V6ONLY, (char*)&one, sizeof(one)); check_res_dump(CR_WARN, res, addr, "setsockopt(IPV6_V6ONLY)"); } res = bind(sockfd, addr->ai_addr, addr->ai_addrlen); check_res_dump(CR_DIE, res, addr, "bind"); if (!udp) { res = listen (sockfd, 50); check_res_dump(CR_DIE, res, addr, "listen"); } return sockfd; } /* Starts listening sockets on specified addresses. * OUT: *sockfd[] pointer to newly-allocated array of listen_endpoint objects * Returns number of addresses bound */ int start_listen_sockets(struct listen_endpoint *sockfd[]) { struct addrinfo *addr, *start_addr; char buf[NI_MAXHOST]; int i, res; int num_addr = 0, keepalive = 0, udp = 0; int sd_socks = 0; sd_socks = get_fd_sockets(sockfd); if (sd_socks > 0) { return sd_socks; } *sockfd = NULL; print_message(msg_config, "Listening to:\n"); for (i = 0; i < cfg.listen_len; i++) { keepalive = cfg.listen[i].keepalive; udp = cfg.listen[i].is_udp; res = resolve_split_name(&start_addr, cfg.listen[i].host, cfg.listen[i].port); if (res) exit(4); for (addr = start_addr; addr; addr = addr->ai_next) { num_addr++; *sockfd = realloc(*sockfd, num_addr * sizeof(*sockfd[0])); (*sockfd)[num_addr-1].socketfd = listen_single_addr(addr, keepalive, udp); (*sockfd)[num_addr-1].type = udp ? SOCK_DGRAM : SOCK_STREAM; print_message(msg_config, "%d:\t%s\t[%s] [%s]\n", (*sockfd)[num_addr-1].socketfd, sprintaddr(buf, sizeof(buf), addr), cfg.listen[i].keepalive ? "keepalive" : "", cfg.listen[i].is_udp ? "udp" : ""); } freeaddrinfo(start_addr); } return num_addr; } /* returns 1 if given address is on the local machine: iterate through all * network interfaces and check their addresses */ int is_same_machine(struct addrinfo* from) { struct ifaddrs *ifaddrs_p = NULL, *ifa; int match = 0; getifaddrs(&ifaddrs_p); for (ifa = ifaddrs_p; ifa != NULL; ifa = ifa->ifa_next) { if (!ifa->ifa_addr) continue; if (from->ai_addr->sa_family == ifa->ifa_addr->sa_family) { int family = ifa->ifa_addr->sa_family; if (family == AF_INET) { struct sockaddr_in *from_addr = (struct sockaddr_in*)from->ai_addr; struct sockaddr_in *ifa_addr = (struct sockaddr_in*)ifa->ifa_addr; if (from_addr->sin_addr.s_addr == ifa_addr->sin_addr.s_addr) { match = 1; break; } } else if (family == AF_INET6) { struct sockaddr_in6 *from_addr = (struct sockaddr_in6*)from->ai_addr; struct sockaddr_in6 *ifa_addr = (struct sockaddr_in6*)ifa->ifa_addr; if (!memcmp(from_addr->sin6_addr.s6_addr, ifa_addr->sin6_addr.s6_addr, 16)) { match = 1; break; } } } } freeifaddrs(ifaddrs_p); return match; } /* Transparent proxying: bind the peer address of fd to the peer address of * fd_from */ #define IP_TRANSPARENT 19 int bind_peer(int fd, int fd_from) { struct addrinfo from; struct sockaddr_storage ss; int res, trans = 1; memset(&from, 0, sizeof(from)); from.ai_addr = (struct sockaddr*)&ss; from.ai_addrlen = sizeof(ss); /* getpeername can fail with ENOTCONN if connection was dropped before we * got here */ res = getpeername(fd_from, from.ai_addr, &from.ai_addrlen); CHECK_RES_RETURN(res, "getpeername", res); /* if the destination is the same machine, there's no need to do bind */ if (is_same_machine(&from)) return 0; #ifndef IP_BINDANY /* use IP_TRANSPARENT */ res = setsockopt(fd, IPPROTO_IP, IP_TRANSPARENT, &trans, sizeof(trans)); CHECK_RES_DIE(res, "setsockopt IP_TRANSPARENT"); #else if (from.ai_addr->sa_family==AF_INET) { /* IPv4 */ res = setsockopt(fd, IPPROTO_IP, IP_BINDANY, &trans, sizeof(trans)); CHECK_RES_RETURN(res, "setsockopt IP_BINDANY", res); #ifdef IPV6_BINDANY } else { /* IPv6 */ res = setsockopt(fd, IPPROTO_IPV6, IPV6_BINDANY, &trans, sizeof(trans)); CHECK_RES_RETURN(res, "setsockopt IPV6_BINDANY", res); #endif /* IPV6_BINDANY */ } #endif /* IP_TRANSPARENT / IP_BINDANY */ res = bind(fd, from.ai_addr, from.ai_addrlen); if (res == -1) { if (errno != EADDRINUSE) { print_message(msg_system_error, "%s:%d:%s:%d:%s\n", __FILE__, __LINE__, "bind", errno, strerror(errno)); return res; } /* * If there is more than one transparent mode proxy going on, such as * using sslh as the target of stunnel also in transparent mode, then * the (ip,port) combination will already be bound for the previous application. * In that case, the best we can do is bind with a different port. * This does mean the local server can't use the ident protocol as the port will * have changed, but most people won't care. * Also note that stunnel uses the same logic for the same situation. */ ((struct sockaddr_in *)from.ai_addr)->sin_port = 0; res = bind(fd, from.ai_addr, from.ai_addrlen); CHECK_RES_RETURN(res, "bind", res); } return 0; } /* Make the file descriptor non-block */ int set_nonblock(int fd) { int flags; flags = fcntl(fd, F_GETFL); CHECK_RES_RETURN(flags, "fcntl", -1); flags |= O_NONBLOCK; flags = fcntl(fd, F_SETFL, flags); CHECK_RES_RETURN(flags, "fcntl", -1); return flags; } /* Connect to first address that works and returns a file descriptor, or -1 if * none work. * If transparent proxying is on, use fd_from peer address on external address * of new file descriptor. */ int connect_addr(struct connection *cnx, int fd_from, connect_blocking blocking) { struct addrinfo *a, from; struct sockaddr_storage ss; char buf[NI_MAXHOST]; int fd, res, one; int transparent = cnx->proto->transparent || cfg.transparent; memset(&from, 0, sizeof(from)); from.ai_addr = (struct sockaddr*)&ss; from.ai_addrlen = sizeof(ss); res = getpeername(fd_from, from.ai_addr, &from.ai_addrlen); CHECK_RES_RETURN(res, "getpeername", res); if (cnx->proto->resolve_on_forward) { resolve_split_name(&(cnx->proto->saddr), cnx->proto->host, cnx->proto->port); } for (a = cnx->proto->saddr; a; a = a->ai_next) { /* When transparent, make sure both connections use the same address family */ if (transparent && a->ai_family != from.ai_addr->sa_family) continue; print_message(msg_connections_try, "trying to connect to %s family %d len %d\n", sprintaddr(buf, sizeof(buf), a), a->ai_addr->sa_family, a->ai_addrlen); /* XXX Needs to match ai_family from fd_from when being transparent! */ fd = socket(a->ai_family, SOCK_STREAM, 0); if (fd == -1) { print_message(msg_connections_error, "forward to %s failed:socket: %s\n", cnx->proto->name, strerror(errno)); } else { one = 1; setsockopt(fd, IPPROTO_TCP, TCP_FASTOPEN_CONNECT, &one, sizeof(one)); /* no need to check return value; if it's not supported, that's okay */ if (blocking == NON_BLOCKING) { set_nonblock(fd); } if (transparent) { res = bind_peer(fd, fd_from); if (res == -1) close(fd); CHECK_RES_RETURN(res, "bind_peer", res); } res = connect(fd, a->ai_addr, a->ai_addrlen); /* EINPROGRESS indicates it might take time. If it eventually * fails, it'll be caught as a failed read */ if ((res == -1) && (errno != EINPROGRESS)) { print_message(msg_connections_error, "forward to %s failed:connect: %s\n", cnx->proto->name, strerror(errno)); close(fd); continue; /* Try the next address */ } if (cnx->proto->keepalive) { res = setsockopt(fd, SOL_SOCKET, SO_KEEPALIVE, (char*)&one, sizeof(one)); CHECK_RES_RETURN(res, "setsockopt(SO_KEEPALIVE)", res); } return fd; } } return -1; } /* Store some data to write to the queue later */ int defer_write(struct queue *q, void* data, ssize_t data_size) { char *p; ptrdiff_t data_offset = q->deferred_data - q->begin_deferred_data; print_message(msg_fd, "writing deferred on fd %d\n", q->fd); p = realloc(q->begin_deferred_data, data_offset + q->deferred_data_size + data_size); CHECK_ALLOC(p, "realloc"); q->begin_deferred_data = p; q->deferred_data = p + data_offset; p += data_offset + q->deferred_data_size; q->deferred_data_size += (int)data_size; memcpy(p, data, data_size); return 0; } /* tries to flush some of the data for specified queue * Upon success, the number of bytes written is returned. * Upon failure, -1 returned (e.g. connexion closed) * */ int flush_deferred(struct queue *q) { ssize_t n; print_message(msg_fd, "flushing deferred data to fd %d\n", q->fd); n = write(q->fd, q->deferred_data, q->deferred_data_size); if (n == -1) return (int)n; if (n == q->deferred_data_size) { /* All has been written -- release the memory */ free(q->begin_deferred_data); q->begin_deferred_data = NULL; q->deferred_data = NULL; q->deferred_data_size = 0; } else { /* There is data left */ q->deferred_data += n; q->deferred_data_size -= (int)n; } return (int)n; } void init_cnx(struct connection *cnx) { memset(cnx, 0, sizeof(*cnx)); cnx->q[0].fd = -1; cnx->q[1].fd = -1; cnx->proto = NULL; } void dump_connection(struct connection *cnx) { print_message(msg_int_error, "type: %s\n", cnx->type == SOCK_DGRAM ? "UDP" : "TCP"); print_message(msg_int_error, "state: %d\n", cnx->state); print_message(msg_int_error, "0: fd %d, %d deferred\n", cnx->q[0].fd, cnx->q[0].deferred_data_size); hexdump(msg_int_error, cnx->q[0].deferred_data, cnx->q[0].deferred_data_size); print_message(msg_int_error, "1: fd %d, %d deferred\n", cnx->q[1].fd, cnx->q[1].deferred_data_size); hexdump(msg_int_error, cnx->q[1].deferred_data, cnx->q[1].deferred_data_size); } /* * moves data from one fd to other * * returns number of bytes copied if success * returns 0 (FD_CNXCLOSED) if incoming socket closed * returns FD_NODATA if no data was available * returns FD_STALLED if data was read, could not be written, and has been * stored in temporary buffer. */ int fd2fd(struct queue *target_q, struct queue *from_q) { char buffer[BUFSIZ]; int target, from; ssize_t size_r, size_w; target = target_q->fd; from = from_q->fd; size_r = read(from, buffer, sizeof(buffer)); if (size_r == -1) { switch (errno) { case EAGAIN: return FD_NODATA; case ECONNRESET: case ENOTSOCK: case EPIPE: return FD_CNXCLOSED; } } CHECK_RES_RETURN(size_r, "read",FD_CNXCLOSED); if (size_r == 0) return FD_CNXCLOSED; size_w = write(target, buffer, size_r); /* process -1 when we know how to deal with it */ if (size_w == -1) { switch (errno) { case EAGAIN: /* write blocked: Defer data */ defer_write(target_q, buffer, size_r); return FD_STALLED; case ECONNRESET: case EPIPE: /* remote end closed -- drop the connection */ return FD_CNXCLOSED; } } else if (size_w < size_r) { /* incomplete write -- defer the rest of the data */ defer_write(target_q, buffer + size_w, size_r - size_w); return FD_STALLED; } CHECK_RES_RETURN(size_w, "write", FD_CNXCLOSED); return (int)size_w; } /* returns a string that prints the IP and port of the sockaddr */ char* sprintaddr(char* buf, size_t size, struct addrinfo *a) { char host[NI_MAXHOST], serv[NI_MAXSERV]; int res; res = getnameinfo(a->ai_addr, a->ai_addrlen, host, sizeof(host), serv, sizeof(serv), cfg.numeric ? NI_NUMERICHOST | NI_NUMERICSERV : 0 ); if (res) { print_message(msg_system_error, "sprintaddr:getnameinfo: %s\n", gai_strerror(res)); /* Name resolution failed: do it numerically instead */ res = getnameinfo(a->ai_addr, a->ai_addrlen, host, sizeof(host), serv, sizeof(serv), NI_NUMERICHOST | NI_NUMERICSERV); /* should not fail but... */ if (res) { print_message(msg_system_error, "sprintaddr:getnameinfo(NUM): %s\n", gai_strerror(res)); strcpy(host, "?"); strcpy(serv, "?"); } } snprintf(buf, size, "%s:%s", host, serv); return buf; } /* Turns a hostname and port (or service) into a list of struct addrinfo * On success, returns 0 * On failure, returns -1 or one of getaddrinfo() codes */ int resolve_split_name(struct addrinfo **out, char* host, char* serv) { struct addrinfo hint; char *end; int res; memset(&hint, 0, sizeof(hint)); hint.ai_family = PF_UNSPEC; hint.ai_socktype = SOCK_STREAM; /* If it is a RFC-Compliant IPv6 address ("[1234::12]:443"), remove brackets * around IP address */ if (host[0] == '[') { end = strrchr(host, ']'); if (!end) { print_message(msg_config_error, "%s: no closing bracket in IPv6 address?\n", host); return -1; } host++; /* skip first bracket */ *end = 0; /* remove last bracket */ } res = getaddrinfo(host, serv, &hint, out); if (res) print_message(msg_system_error, "resolve_split_name: %s `%s:%s'\n", gai_strerror(res), host, serv); return res; } /* turns a "hostname:port" string into a list of struct addrinfo; out: list of newly allocated addrinfo (see getaddrinfo(3)); freeaddrinfo(3) when done fullname: input string -- it gets clobbered */ void resolve_name(struct addrinfo **out, char* fullname) { char *serv, *host; int res; /* Find port */ char *sep = strrchr(fullname, ':'); if (!sep) { /* No separator: parameter is just a port */ print_message(msg_config_error, "%s: names must be fully specified as hostname:port\n", fullname); exit(1); } serv = sep+1; *sep = 0; host = fullname; res = resolve_split_name(out, host, serv); if (res) { print_message(msg_config_error, "%s `%s'\n", gai_strerror(res), fullname); if (res == EAI_SERVICE) print_message(msg_config_error, "(Check you have specified all ports)\n"); exit(4); } } /* Fills a connection description; returns 0 on failure */ int get_connection_desc(struct connection_desc* desc, const struct connection *cnx) { int res; struct addrinfo addr; struct sockaddr_storage ss; addr.ai_addr = (struct sockaddr*)&ss; addr.ai_addrlen = sizeof(ss); res = getpeername(cnx->q[0].fd, addr.ai_addr, &addr.ai_addrlen); if (res == -1) return 0; /* Can happen if connection drops before we get here. In that case, don't log anything (there is no connection) */ sprintaddr(desc->peer, sizeof(desc->peer), &addr); addr.ai_addrlen = sizeof(ss); res = getsockname(cnx->q[0].fd, addr.ai_addr, &addr.ai_addrlen); if (res == -1) return 0; sprintaddr(desc->service, sizeof(desc->service), &addr); addr.ai_addrlen = sizeof(ss); res = getpeername(cnx->q[1].fd, addr.ai_addr, &addr.ai_addrlen); if (res == -1) return 0; sprintaddr(desc->target, sizeof(desc->target), &addr); addr.ai_addrlen = sizeof(ss); res = getsockname(cnx->q[1].fd, addr.ai_addr, &addr.ai_addrlen); if (res == -1) return 0; sprintaddr(desc->local, sizeof(desc->local), &addr); return 1; } void set_proctitle_shovel(struct connection_desc* desc, const struct connection *cnx) { #ifdef LIBBSD struct connection_desc d; if (!desc) { desc = &d; get_connection_desc(desc, cnx); } setproctitle("shovel %s %s->%s => %s->%s", cnx->proto->name, desc->peer, desc->service, desc->local, desc->target); #endif } /* libwrap (tcpd): check the connection is legal. This is necessary because * the actual server will only see a connection coming from localhost and can't * apply the rules itself. * * Returns -1 if access is denied, 0 otherwise */ int check_access_rights(int in_socket, const char* service) { #ifdef LIBWRAP union { struct sockaddr saddr; struct sockaddr_storage ss; } peer; socklen_t size = sizeof(peer); char addr_str[NI_MAXHOST], host[NI_MAXHOST]; int res; res = getpeername(in_socket, &peer.saddr, &size); CHECK_RES_RETURN(res, "getpeername", res); /* extract peer address */ res = getnameinfo(&peer.saddr, size, addr_str, sizeof(addr_str), NULL, 0, NI_NUMERICHOST); if (res) { print_message(msg_system_error, "getnameinfo(NI_NUMERICHOST):%s\n", gai_strerror(res)); strcpy(addr_str, STRING_UNKNOWN); } /* extract peer name */ strcpy(host, STRING_UNKNOWN); if (!cfg.numeric) { res = getnameinfo(&peer.saddr, size, host, sizeof(host), NULL, 0, NI_NAMEREQD); if (res) { print_message(msg_system_error, "getnameinfo(NI_NAMEREQD):%s\n", gai_strerror(res)); } } if (!hosts_ctl(service, host, addr_str, STRING_UNKNOWN)) { print_message(msg_connections, "connection from %s(%s): access denied", host, addr_str); close(in_socket); return -1; } #endif return 0; } void setup_signals(void) { int res; struct sigaction action; /* Request no SIGCHLD is sent upon termination of * the children */ memset(&action, 0, sizeof(action)); action.sa_handler = NULL; action.sa_flags = SA_NOCLDWAIT; res = sigaction(SIGCHLD, &action, NULL); CHECK_RES_DIE(res, "sigaction"); /* Set SIGTERM to exit. For some reason if it's not set explicitly, * coverage information is lost when killing the process */ memset(&action, 0, sizeof(action)); action.sa_handler = exit; res = sigaction(SIGTERM, &action, NULL); CHECK_RES_DIE(res, "sigaction"); /* Ignore SIGPIPE . */ action.sa_handler = SIG_IGN; res = sigaction(SIGPIPE, &action, NULL); CHECK_RES_DIE(res, "sigaction"); } /* Ask OS to keep capabilities over a setuid(nonzero) */ void set_keepcaps(int val) { #ifdef LIBCAP int res; res = prctl(PR_SET_KEEPCAPS, val, 0, 0, 0); if (res) { perror("prctl"); exit(1); } #endif } /* Returns true if anything requires transparent proxying. */ static int use_transparent(void) { #ifdef LIBCAP if (cfg.transparent) return 1; for (int i = 0; i < cfg.protocols_len; i++) if (cfg.protocols[i].transparent) return 1; #endif return 0; } /* set needed capabilities for effective and permitted, clear rest * IN: cap_net_admin: set to 1 to set CAP_NET_RAW * */ void set_capabilities(int cap_net_admin) { #ifdef LIBCAP int res; cap_t caps; cap_value_t cap_list[10]; int ncap = 0; if (cap_net_admin) cap_list[ncap++] = CAP_NET_RAW; caps = cap_init(); #define _cap_set_flag(flag) do { \ res = cap_clear_flag(caps, flag); \ CHECK_RES_DIE(res, "cap_clear_flag(" #flag ")"); \ if (ncap > 0) { \ res = cap_set_flag(caps, flag, ncap, cap_list, CAP_SET); \ CHECK_RES_DIE(res, "cap_set_flag(" #flag ")"); \ } \ } while(0) _cap_set_flag(CAP_EFFECTIVE); _cap_set_flag(CAP_PERMITTED); #undef _cap_set_flag res = cap_set_proc(caps); CHECK_RES_DIE(res, "cap_set_proc"); res = cap_free(caps); if (res) { perror("cap_free"); exit(1); } #endif } /* We don't want to run as root -- drop privileges if required */ void drop_privileges(const char* user_name, const char* chroot_path) { int res; struct passwd *pw = NULL; if (user_name) { pw = getpwnam(user_name); if (!pw) { print_message(msg_config_error, "%s: not found\n", user_name); exit(2); } print_message(msg_config, "turning into %s\n", user_name); } if (chroot_path) { print_message(msg_config, "chrooting into %s\n", chroot_path); res = chroot(chroot_path); CHECK_RES_DIE(res, "chroot"); } if (user_name) { set_keepcaps(1); /* remove extraneous groups in case we belong to several extra groups * that may have unwanted rights. If non-root when calling setgroups(), * it fails, which is fine because... we have no unwanted rights * (see POS36-C for security context) * */ setgroups(0, NULL); res = setgid(pw->pw_gid); CHECK_RES_DIE(res, "setgid"); res = setuid(pw->pw_uid); CHECK_RES_DIE(res, "setuid"); set_capabilities(use_transparent()); set_keepcaps(0); } } /* Writes my PID */ void write_pid_file(const char* pidfile) { FILE *f; int res; f = fopen(pidfile, "w"); if (!f) { print_message(msg_system_error, "write_pid_file: %s: %s\n", pidfile, strerror(errno)); return; } res = fprintf(f, "%d\n", getpid()); if (res < 0) { print_message(msg_system_error, "write_pid_file: fprintf: %s\n", strerror(errno)); return; } res = fclose(f); if (res == EOF) { print_message(msg_system_error, "write_pid_file: fclose: %s\n", strerror(errno)); return; } } sslh-2.1.4/common.h000066400000000000000000000117521463704647400141550ustar00rootroot00000000000000#ifndef COMMON_H #define COMMON_H /* FD_SETSIZE is 64 on Cygwin, which is really low. Just redefining it is * enough for the macros to adapt (http://support.microsoft.com/kb/111855) */ #ifdef __CYGWIN__ #undef FD_SETSIZE #define FD_SETSIZE 4096 #endif #define _GNU_SOURCE #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #ifdef LIBCAP #include #include #endif #include "version.h" #define MAX(a, b) (((a) > (b)) ? (a) : (b)) #define CHECK_RES_DIE(res, str) \ if (res == -1) { \ print_message(msg_system_error, "%s:%d:", __FILE__, __LINE__); \ perror(str); \ exit(1); \ } #define CHECK_RES_RETURN(res, str, ret) \ if (res == -1) { \ print_message(msg_system_error, "%s:%d:%s:%d:%s\n", __FILE__, __LINE__, str, errno, strerror(errno)); \ return ret; \ } #define CHECK_ALLOC(a, str) \ if (!a) { \ print_message(msg_system_error, "%s:%d:", __FILE__, __LINE__); \ perror(str); \ exit(1); \ } #define ARRAY_SIZE(a) (sizeof(a) / sizeof(a[0])) #if 1 #define TRACE fprintf(stderr, "%s:%d\n", __FILE__, __LINE__); #else #define TRACE #endif #ifndef IP_FREEBIND #define IP_FREEBIND 0 #endif #ifndef TCP_FASTOPEN #define TCP_FASTOPEN 0 #endif #ifndef TCP_FASTOPEN_CONNECT #define TCP_FASTOPEN_CONNECT 30 /* Attempt FastOpen with connect. */ #endif enum connection_state { ST_PROBING=1, /* Waiting for timeout to find where to forward */ ST_SHOVELING /* Connexion is established */ }; /* A 'queue' is composed of a file descriptor (which can be read from or * written to), and a queue for deferred write data */ struct queue { int fd; void *begin_deferred_data; void *deferred_data; int deferred_data_size; }; /* Double linked list for timeout management */ typedef struct { struct connection* head; struct connection* tail; } dl_list; struct connection { int type; /* SOCK_DGRAM | SOCK_STREAM */ struct sslhcfg_protocols_item* proto; /* Where to connect to */ /* SOCK_STREAM */ enum connection_state state; time_t probe_timeout; /* q[0]: queue for external connection (client); * q[1]: queue for internal connection (httpd or sshd); * */ struct queue q[2]; /* SOCK_DGRAM */ struct sockaddr_storage client_addr; /* Contains the remote client address */ socklen_t addrlen; int local_endpoint; /* Contains the local address */ time_t last_active; /* double linked list of timeouts */ struct connection *timeout_prev, *timeout_next; /* We need one local socket for each target server, so we know where to * forward server responses */ int target_sock; }; struct listen_endpoint { int socketfd; /* file descriptor of listening socket */ int type; /* SOCK_DGRAM | SOCK_STREAM */ }; #define FD_CNXCLOSED 0 #define FD_NODATA -1 #define FD_STALLED -2 /* String description of a connection */ #define MAX_NAMELENGTH (NI_MAXHOST + NI_MAXSERV + 1) struct connection_desc { char peer[MAX_NAMELENGTH], service[MAX_NAMELENGTH], local[MAX_NAMELENGTH], target[MAX_NAMELENGTH]; }; typedef enum { NON_BLOCKING = 0, BLOCKING = 1 } connect_blocking; /* common.c */ void init_cnx(struct connection *cnx); int set_nonblock(int fd); int connect_addr(struct connection *cnx, int fd_from, connect_blocking blocking); int fd2fd(struct queue *target, struct queue *from); char* sprintaddr(char* buf, size_t size, struct addrinfo *a); void resolve_name(struct addrinfo **out, char* fullname); int get_connection_desc(struct connection_desc* desc, const struct connection *cnx); void log_connection(struct connection_desc* desc, const struct connection *cnx); void set_proctitle_shovel(struct connection_desc* desc, const struct connection *cnx); int check_access_rights(int in_socket, const char* service); void setup_signals(void); void setup_syslog(const char* bin_name); void drop_privileges(const char* user_name, const char* chroot_path); void set_capabilities(int cap_net_admin); void write_pid_file(const char* pidfile); void dump_connection(struct connection *cnx); int resolve_split_name(struct addrinfo **out, char* hostname, char* port); int start_listen_sockets(struct listen_endpoint *sockfd[]); int defer_write(struct queue *q, void* data, ssize_t data_size); int flush_deferred(struct queue *q); extern struct sslhcfg_item cfg; extern struct addrinfo *addr_listen; extern const char* server_type; /* sslh-fork.c */ void start_shoveler(int); void main_loop(struct listen_endpoint *listen_sockets, int num_addr_listen); /* landlock.c */ void setup_landlock(void); #endif sslh-2.1.4/config.h.in000066400000000000000000000002111463704647400145230ustar00rootroot00000000000000 #ifndef CONFIG_H /* Template for config.h, filled by `configure`. */ /* Landlock sandboxing Linux LSM */ #undef HAVE_LANDLOCK #endif sslh-2.1.4/configure000077500000000000000000003723601463704647400144300ustar00rootroot00000000000000#! /bin/sh # Guess values for system-dependent variables and create Makefiles. # Generated by GNU Autoconf 2.71. # # # Copyright (C) 1992-1996, 1998-2017, 2020-2021 Free Software Foundation, # Inc. # # # This configure script is free software; the Free Software Foundation # gives unlimited permission to copy, distribute and modify it. ## -------------------- ## ## M4sh Initialization. ## ## -------------------- ## # Be more Bourne compatible DUALCASE=1; export DUALCASE # for MKS sh as_nop=: if test ${ZSH_VERSION+y} && (emulate sh) >/dev/null 2>&1 then : emulate sh NULLCMD=: # Pre-4.2 versions of Zsh do word splitting on ${1+"$@"}, which # is contrary to our usage. Disable this feature. alias -g '${1+"$@"}'='"$@"' setopt NO_GLOB_SUBST else $as_nop case `(set -o) 2>/dev/null` in #( *posix*) : set -o posix ;; #( *) : ;; esac fi # Reset variables that may have inherited troublesome values from # the environment. # IFS needs to be set, to space, tab, and newline, in precisely that order. # (If _AS_PATH_WALK were called with IFS unset, it would have the # side effect of setting IFS to empty, thus disabling word splitting.) # Quoting is to prevent editors from complaining about space-tab. as_nl=' ' export as_nl IFS=" "" $as_nl" PS1='$ ' PS2='> ' PS4='+ ' # Ensure predictable behavior from utilities with locale-dependent output. LC_ALL=C export LC_ALL LANGUAGE=C export LANGUAGE # We cannot yet rely on "unset" to work, but we need these variables # to be unset--not just set to an empty or harmless value--now, to # avoid bugs in old shells (e.g. pre-3.0 UWIN ksh). This construct # also avoids known problems related to "unset" and subshell syntax # in other old shells (e.g. bash 2.01 and pdksh 5.2.14). for as_var in BASH_ENV ENV MAIL MAILPATH CDPATH do eval test \${$as_var+y} \ && ( (unset $as_var) || exit 1) >/dev/null 2>&1 && unset $as_var || : done # Ensure that fds 0, 1, and 2 are open. if (exec 3>&0) 2>/dev/null; then :; else exec 0&1) 2>/dev/null; then :; else exec 1>/dev/null; fi if (exec 3>&2) ; then :; else exec 2>/dev/null; fi # The user is always right. if ${PATH_SEPARATOR+false} :; then PATH_SEPARATOR=: (PATH='/bin;/bin'; FPATH=$PATH; sh -c :) >/dev/null 2>&1 && { (PATH='/bin:/bin'; FPATH=$PATH; sh -c :) >/dev/null 2>&1 || PATH_SEPARATOR=';' } fi # Find who we are. Look in the path if we contain no directory separator. as_myself= case $0 in #(( *[\\/]* ) as_myself=$0 ;; *) as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS case $as_dir in #((( '') as_dir=./ ;; */) ;; *) as_dir=$as_dir/ ;; esac test -r "$as_dir$0" && as_myself=$as_dir$0 && break done IFS=$as_save_IFS ;; esac # We did not find ourselves, most probably we were run as `sh COMMAND' # in which case we are not to be found in the path. if test "x$as_myself" = x; then as_myself=$0 fi if test ! -f "$as_myself"; then printf "%s\n" "$as_myself: error: cannot find myself; rerun with an absolute file name" >&2 exit 1 fi # Use a proper internal environment variable to ensure we don't fall # into an infinite loop, continuously re-executing ourselves. if test x"${_as_can_reexec}" != xno && test "x$CONFIG_SHELL" != x; then _as_can_reexec=no; export _as_can_reexec; # We cannot yet assume a decent shell, so we have to provide a # neutralization value for shells without unset; and this also # works around shells that cannot unset nonexistent variables. # Preserve -v and -x to the replacement shell. BASH_ENV=/dev/null ENV=/dev/null (unset BASH_ENV) >/dev/null 2>&1 && unset BASH_ENV ENV case $- in # (((( *v*x* | *x*v* ) as_opts=-vx ;; *v* ) as_opts=-v ;; *x* ) as_opts=-x ;; * ) as_opts= ;; esac exec $CONFIG_SHELL $as_opts "$as_myself" ${1+"$@"} # Admittedly, this is quite paranoid, since all the known shells bail # out after a failed `exec'. printf "%s\n" "$0: could not re-execute with $CONFIG_SHELL" >&2 exit 255 fi # We don't want this to propagate to other subprocesses. { _as_can_reexec=; unset _as_can_reexec;} if test "x$CONFIG_SHELL" = x; then as_bourne_compatible="as_nop=: if test \${ZSH_VERSION+y} && (emulate sh) >/dev/null 2>&1 then : emulate sh NULLCMD=: # Pre-4.2 versions of Zsh do word splitting on \${1+\"\$@\"}, which # is contrary to our usage. Disable this feature. alias -g '\${1+\"\$@\"}'='\"\$@\"' setopt NO_GLOB_SUBST else \$as_nop case \`(set -o) 2>/dev/null\` in #( *posix*) : set -o posix ;; #( *) : ;; esac fi " as_required="as_fn_return () { (exit \$1); } as_fn_success () { as_fn_return 0; } as_fn_failure () { as_fn_return 1; } as_fn_ret_success () { return 0; } as_fn_ret_failure () { return 1; } exitcode=0 as_fn_success || { exitcode=1; echo as_fn_success failed.; } as_fn_failure && { exitcode=1; echo as_fn_failure succeeded.; } as_fn_ret_success || { exitcode=1; echo as_fn_ret_success failed.; } as_fn_ret_failure && { exitcode=1; echo as_fn_ret_failure succeeded.; } if ( set x; as_fn_ret_success y && test x = \"\$1\" ) then : else \$as_nop exitcode=1; echo positional parameters were not saved. fi test x\$exitcode = x0 || exit 1 blah=\$(echo \$(echo blah)) test x\"\$blah\" = xblah || exit 1 test -x / || exit 1" as_suggested=" as_lineno_1=";as_suggested=$as_suggested$LINENO;as_suggested=$as_suggested" as_lineno_1a=\$LINENO as_lineno_2=";as_suggested=$as_suggested$LINENO;as_suggested=$as_suggested" as_lineno_2a=\$LINENO eval 'test \"x\$as_lineno_1'\$as_run'\" != \"x\$as_lineno_2'\$as_run'\" && test \"x\`expr \$as_lineno_1'\$as_run' + 1\`\" = \"x\$as_lineno_2'\$as_run'\"' || exit 1" if (eval "$as_required") 2>/dev/null then : as_have_required=yes else $as_nop as_have_required=no fi if test x$as_have_required = xyes && (eval "$as_suggested") 2>/dev/null then : else $as_nop as_save_IFS=$IFS; IFS=$PATH_SEPARATOR as_found=false for as_dir in /bin$PATH_SEPARATOR/usr/bin$PATH_SEPARATOR$PATH do IFS=$as_save_IFS case $as_dir in #((( '') as_dir=./ ;; */) ;; *) as_dir=$as_dir/ ;; esac as_found=: case $as_dir in #( /*) for as_base in sh bash ksh sh5; do # Try only shells that exist, to save several forks. as_shell=$as_dir$as_base if { test -f "$as_shell" || test -f "$as_shell.exe"; } && as_run=a "$as_shell" -c "$as_bourne_compatible""$as_required" 2>/dev/null then : CONFIG_SHELL=$as_shell as_have_required=yes if as_run=a "$as_shell" -c "$as_bourne_compatible""$as_suggested" 2>/dev/null then : break 2 fi fi done;; esac as_found=false done IFS=$as_save_IFS if $as_found then : else $as_nop if { test -f "$SHELL" || test -f "$SHELL.exe"; } && as_run=a "$SHELL" -c "$as_bourne_compatible""$as_required" 2>/dev/null then : CONFIG_SHELL=$SHELL as_have_required=yes fi fi if test "x$CONFIG_SHELL" != x then : export CONFIG_SHELL # We cannot yet assume a decent shell, so we have to provide a # neutralization value for shells without unset; and this also # works around shells that cannot unset nonexistent variables. # Preserve -v and -x to the replacement shell. BASH_ENV=/dev/null ENV=/dev/null (unset BASH_ENV) >/dev/null 2>&1 && unset BASH_ENV ENV case $- in # (((( *v*x* | *x*v* ) as_opts=-vx ;; *v* ) as_opts=-v ;; *x* ) as_opts=-x ;; * ) as_opts= ;; esac exec $CONFIG_SHELL $as_opts "$as_myself" ${1+"$@"} # Admittedly, this is quite paranoid, since all the known shells bail # out after a failed `exec'. printf "%s\n" "$0: could not re-execute with $CONFIG_SHELL" >&2 exit 255 fi if test x$as_have_required = xno then : printf "%s\n" "$0: This script requires a shell more modern than all" printf "%s\n" "$0: the shells that I found on your system." if test ${ZSH_VERSION+y} ; then printf "%s\n" "$0: In particular, zsh $ZSH_VERSION has bugs and should" printf "%s\n" "$0: be upgraded to zsh 4.3.4 or later." else printf "%s\n" "$0: Please tell bug-autoconf@gnu.org about your system, $0: including any error possibly output before this $0: message. Then install a modern shell, or manually run $0: the script under such a shell if you do have one." fi exit 1 fi fi fi SHELL=${CONFIG_SHELL-/bin/sh} export SHELL # Unset more variables known to interfere with behavior of common tools. CLICOLOR_FORCE= GREP_OPTIONS= unset CLICOLOR_FORCE GREP_OPTIONS ## --------------------- ## ## M4sh Shell Functions. ## ## --------------------- ## # as_fn_unset VAR # --------------- # Portably unset VAR. as_fn_unset () { { eval $1=; unset $1;} } as_unset=as_fn_unset # as_fn_set_status STATUS # ----------------------- # Set $? to STATUS, without forking. as_fn_set_status () { return $1 } # as_fn_set_status # as_fn_exit STATUS # ----------------- # Exit the shell with STATUS, even in a "trap 0" or "set -e" context. as_fn_exit () { set +e as_fn_set_status $1 exit $1 } # as_fn_exit # as_fn_nop # --------- # Do nothing but, unlike ":", preserve the value of $?. as_fn_nop () { return $? } as_nop=as_fn_nop # as_fn_mkdir_p # ------------- # Create "$as_dir" as a directory, including parents if necessary. as_fn_mkdir_p () { case $as_dir in #( -*) as_dir=./$as_dir;; esac test -d "$as_dir" || eval $as_mkdir_p || { as_dirs= while :; do case $as_dir in #( *\'*) as_qdir=`printf "%s\n" "$as_dir" | sed "s/'/'\\\\\\\\''/g"`;; #'( *) as_qdir=$as_dir;; esac as_dirs="'$as_qdir' $as_dirs" as_dir=`$as_dirname -- "$as_dir" || $as_expr X"$as_dir" : 'X\(.*[^/]\)//*[^/][^/]*/*$' \| \ X"$as_dir" : 'X\(//\)[^/]' \| \ X"$as_dir" : 'X\(//\)$' \| \ X"$as_dir" : 'X\(/\)' \| . 2>/dev/null || printf "%s\n" X"$as_dir" | sed '/^X\(.*[^/]\)\/\/*[^/][^/]*\/*$/{ s//\1/ q } /^X\(\/\/\)[^/].*/{ s//\1/ q } /^X\(\/\/\)$/{ s//\1/ q } /^X\(\/\).*/{ s//\1/ q } s/.*/./; q'` test -d "$as_dir" && break done test -z "$as_dirs" || eval "mkdir $as_dirs" } || test -d "$as_dir" || as_fn_error $? "cannot create directory $as_dir" } # as_fn_mkdir_p # as_fn_executable_p FILE # ----------------------- # Test if FILE is an executable regular file. as_fn_executable_p () { test -f "$1" && test -x "$1" } # as_fn_executable_p # as_fn_append VAR VALUE # ---------------------- # Append the text in VALUE to the end of the definition contained in VAR. Take # advantage of any shell optimizations that allow amortized linear growth over # repeated appends, instead of the typical quadratic growth present in naive # implementations. if (eval "as_var=1; as_var+=2; test x\$as_var = x12") 2>/dev/null then : eval 'as_fn_append () { eval $1+=\$2 }' else $as_nop as_fn_append () { eval $1=\$$1\$2 } fi # as_fn_append # as_fn_arith ARG... # ------------------ # Perform arithmetic evaluation on the ARGs, and store the result in the # global $as_val. Take advantage of shells that can avoid forks. The arguments # must be portable across $(()) and expr. if (eval "test \$(( 1 + 1 )) = 2") 2>/dev/null then : eval 'as_fn_arith () { as_val=$(( $* )) }' else $as_nop as_fn_arith () { as_val=`expr "$@" || test $? -eq 1` } fi # as_fn_arith # as_fn_nop # --------- # Do nothing but, unlike ":", preserve the value of $?. as_fn_nop () { return $? } as_nop=as_fn_nop # as_fn_error STATUS ERROR [LINENO LOG_FD] # ---------------------------------------- # Output "`basename $0`: error: ERROR" to stderr. If LINENO and LOG_FD are # provided, also output the error to LOG_FD, referencing LINENO. Then exit the # script with STATUS, using 1 if that was 0. as_fn_error () { as_status=$1; test $as_status -eq 0 && as_status=1 if test "$4"; then as_lineno=${as_lineno-"$3"} as_lineno_stack=as_lineno_stack=$as_lineno_stack printf "%s\n" "$as_me:${as_lineno-$LINENO}: error: $2" >&$4 fi printf "%s\n" "$as_me: error: $2" >&2 as_fn_exit $as_status } # as_fn_error if expr a : '\(a\)' >/dev/null 2>&1 && test "X`expr 00001 : '.*\(...\)'`" = X001; then as_expr=expr else as_expr=false fi if (basename -- /) >/dev/null 2>&1 && test "X`basename -- / 2>&1`" = "X/"; then as_basename=basename else as_basename=false fi if (as_dir=`dirname -- /` && test "X$as_dir" = X/) >/dev/null 2>&1; then as_dirname=dirname else as_dirname=false fi as_me=`$as_basename -- "$0" || $as_expr X/"$0" : '.*/\([^/][^/]*\)/*$' \| \ X"$0" : 'X\(//\)$' \| \ X"$0" : 'X\(/\)' \| . 2>/dev/null || printf "%s\n" X/"$0" | sed '/^.*\/\([^/][^/]*\)\/*$/{ s//\1/ q } /^X\/\(\/\/\)$/{ s//\1/ q } /^X\/\(\/\).*/{ s//\1/ q } s/.*/./; q'` # Avoid depending upon Character Ranges. as_cr_letters='abcdefghijklmnopqrstuvwxyz' as_cr_LETTERS='ABCDEFGHIJKLMNOPQRSTUVWXYZ' as_cr_Letters=$as_cr_letters$as_cr_LETTERS as_cr_digits='0123456789' as_cr_alnum=$as_cr_Letters$as_cr_digits as_lineno_1=$LINENO as_lineno_1a=$LINENO as_lineno_2=$LINENO as_lineno_2a=$LINENO eval 'test "x$as_lineno_1'$as_run'" != "x$as_lineno_2'$as_run'" && test "x`expr $as_lineno_1'$as_run' + 1`" = "x$as_lineno_2'$as_run'"' || { # Blame Lee E. McMahon (1931-1989) for sed's syntax. :-) sed -n ' p /[$]LINENO/= ' <$as_myself | sed ' s/[$]LINENO.*/&-/ t lineno b :lineno N :loop s/[$]LINENO\([^'$as_cr_alnum'_].*\n\)\(.*\)/\2\1\2/ t loop s/-\n.*// ' >$as_me.lineno && chmod +x "$as_me.lineno" || { printf "%s\n" "$as_me: error: cannot create $as_me.lineno; rerun with a POSIX shell" >&2; as_fn_exit 1; } # If we had to re-execute with $CONFIG_SHELL, we're ensured to have # already done that, so ensure we don't try to do so again and fall # in an infinite loop. This has already happened in practice. _as_can_reexec=no; export _as_can_reexec # Don't try to exec as it changes $[0], causing all sort of problems # (the dirname of $[0] is not the place where we might find the # original and so on. Autoconf is especially sensitive to this). . "./$as_me.lineno" # Exit status is that of the last command. exit } # Determine whether it's possible to make 'echo' print without a newline. # These variables are no longer used directly by Autoconf, but are AC_SUBSTed # for compatibility with existing Makefiles. ECHO_C= ECHO_N= ECHO_T= case `echo -n x` in #((((( -n*) case `echo 'xy\c'` in *c*) ECHO_T=' ';; # ECHO_T is single tab character. xy) ECHO_C='\c';; *) echo `echo ksh88 bug on AIX 6.1` > /dev/null ECHO_T=' ';; esac;; *) ECHO_N='-n';; esac # For backward compatibility with old third-party macros, we provide # the shell variables $as_echo and $as_echo_n. New code should use # AS_ECHO(["message"]) and AS_ECHO_N(["message"]), respectively. as_echo='printf %s\n' as_echo_n='printf %s' rm -f conf$$ conf$$.exe conf$$.file if test -d conf$$.dir; then rm -f conf$$.dir/conf$$.file else rm -f conf$$.dir mkdir conf$$.dir 2>/dev/null fi if (echo >conf$$.file) 2>/dev/null; then if ln -s conf$$.file conf$$ 2>/dev/null; then as_ln_s='ln -s' # ... but there are two gotchas: # 1) On MSYS, both `ln -s file dir' and `ln file dir' fail. # 2) DJGPP < 2.04 has no symlinks; `ln -s' creates a wrapper executable. # In both cases, we have to default to `cp -pR'. ln -s conf$$.file conf$$.dir 2>/dev/null && test ! -f conf$$.exe || as_ln_s='cp -pR' elif ln conf$$.file conf$$ 2>/dev/null; then as_ln_s=ln else as_ln_s='cp -pR' fi else as_ln_s='cp -pR' fi rm -f conf$$ conf$$.exe conf$$.dir/conf$$.file conf$$.file rmdir conf$$.dir 2>/dev/null if mkdir -p . 2>/dev/null; then as_mkdir_p='mkdir -p "$as_dir"' else test -d ./-p && rmdir ./-p as_mkdir_p=false fi as_test_x='test -x' as_executable_p=as_fn_executable_p # Sed expression to map a string onto a valid CPP name. as_tr_cpp="eval sed 'y%*$as_cr_letters%P$as_cr_LETTERS%;s%[^_$as_cr_alnum]%_%g'" # Sed expression to map a string onto a valid variable name. as_tr_sh="eval sed 'y%*+%pp%;s%[^_$as_cr_alnum]%_%g'" test -n "$DJDIR" || exec 7<&0 &1 # Name of the host. # hostname on some systems (SVR3.2, old GNU/Linux) returns a bogus exit status, # so uname gets run too. ac_hostname=`(hostname || uname -n) 2>/dev/null | sed 1q` # # Initializations. # ac_default_prefix=/usr/local ac_clean_files= ac_config_libobj_dir=. LIBOBJS= cross_compiling=no subdirs= MFLAGS= MAKEFLAGS= # Identity of this package. PACKAGE_NAME='' PACKAGE_TARNAME='' PACKAGE_VERSION='' PACKAGE_STRING='' PACKAGE_BUGREPORT='' PACKAGE_URL='' # Factoring default headers for most tests. ac_includes_default="\ #include #ifdef HAVE_STDIO_H # include #endif #ifdef HAVE_STDLIB_H # include #endif #ifdef HAVE_STRING_H # include #endif #ifdef HAVE_INTTYPES_H # include #endif #ifdef HAVE_STDINT_H # include #endif #ifdef HAVE_STRINGS_H # include #endif #ifdef HAVE_SYS_TYPES_H # include #endif #ifdef HAVE_SYS_STAT_H # include #endif #ifdef HAVE_UNISTD_H # include #endif" ac_header_c_list= ac_subst_vars='LTLIBOBJS LIBOBJS OBJEXT EXEEXT ac_ct_CC CPPFLAGS LDFLAGS CFLAGS CC target_alias host_alias build_alias LIBS ECHO_T ECHO_N ECHO_C DEFS mandir localedir libdir psdir pdfdir dvidir htmldir infodir docdir oldincludedir includedir runstatedir localstatedir sharedstatedir sysconfdir datadir datarootdir libexecdir sbindir bindir program_transform_name prefix exec_prefix PACKAGE_URL PACKAGE_BUGREPORT PACKAGE_STRING PACKAGE_VERSION PACKAGE_TARNAME PACKAGE_NAME PATH_SEPARATOR SHELL' ac_subst_files='' ac_user_opts=' enable_option_checking ' ac_precious_vars='build_alias host_alias target_alias CC CFLAGS LDFLAGS LIBS CPPFLAGS' # Initialize some variables set by options. ac_init_help= ac_init_version=false ac_unrecognized_opts= ac_unrecognized_sep= # The variables have the same names as the options, with # dashes changed to underlines. cache_file=/dev/null exec_prefix=NONE no_create= no_recursion= prefix=NONE program_prefix=NONE program_suffix=NONE program_transform_name=s,x,x, silent= site= srcdir= verbose= x_includes=NONE x_libraries=NONE # Installation directory options. # These are left unexpanded so users can "make install exec_prefix=/foo" # and all the variables that are supposed to be based on exec_prefix # by default will actually change. # Use braces instead of parens because sh, perl, etc. also accept them. # (The list follows the same order as the GNU Coding Standards.) bindir='${exec_prefix}/bin' sbindir='${exec_prefix}/sbin' libexecdir='${exec_prefix}/libexec' datarootdir='${prefix}/share' datadir='${datarootdir}' sysconfdir='${prefix}/etc' sharedstatedir='${prefix}/com' localstatedir='${prefix}/var' runstatedir='${localstatedir}/run' includedir='${prefix}/include' oldincludedir='/usr/include' docdir='${datarootdir}/doc/${PACKAGE}' infodir='${datarootdir}/info' htmldir='${docdir}' dvidir='${docdir}' pdfdir='${docdir}' psdir='${docdir}' libdir='${exec_prefix}/lib' localedir='${datarootdir}/locale' mandir='${datarootdir}/man' ac_prev= ac_dashdash= for ac_option do # If the previous option needs an argument, assign it. if test -n "$ac_prev"; then eval $ac_prev=\$ac_option ac_prev= continue fi case $ac_option in *=?*) ac_optarg=`expr "X$ac_option" : '[^=]*=\(.*\)'` ;; *=) ac_optarg= ;; *) ac_optarg=yes ;; esac case $ac_dashdash$ac_option in --) ac_dashdash=yes ;; -bindir | --bindir | --bindi | --bind | --bin | --bi) ac_prev=bindir ;; -bindir=* | --bindir=* | --bindi=* | --bind=* | --bin=* | --bi=*) bindir=$ac_optarg ;; -build | --build | --buil | --bui | --bu) ac_prev=build_alias ;; -build=* | --build=* | --buil=* | --bui=* | --bu=*) build_alias=$ac_optarg ;; -cache-file | --cache-file | --cache-fil | --cache-fi \ | --cache-f | --cache- | --cache | --cach | --cac | --ca | --c) ac_prev=cache_file ;; -cache-file=* | --cache-file=* | --cache-fil=* | --cache-fi=* \ | --cache-f=* | --cache-=* | --cache=* | --cach=* | --cac=* | --ca=* | --c=*) cache_file=$ac_optarg ;; --config-cache | -C) cache_file=config.cache ;; -datadir | --datadir | --datadi | --datad) ac_prev=datadir ;; -datadir=* | --datadir=* | --datadi=* | --datad=*) datadir=$ac_optarg ;; -datarootdir | --datarootdir | --datarootdi | --datarootd | --dataroot \ | --dataroo | --dataro | --datar) ac_prev=datarootdir ;; -datarootdir=* | --datarootdir=* | --datarootdi=* | --datarootd=* \ | --dataroot=* | --dataroo=* | --dataro=* | --datar=*) datarootdir=$ac_optarg ;; -disable-* | --disable-*) ac_useropt=`expr "x$ac_option" : 'x-*disable-\(.*\)'` # Reject names that are not valid shell variable names. expr "x$ac_useropt" : ".*[^-+._$as_cr_alnum]" >/dev/null && as_fn_error $? "invalid feature name: \`$ac_useropt'" ac_useropt_orig=$ac_useropt ac_useropt=`printf "%s\n" "$ac_useropt" | sed 's/[-+.]/_/g'` case $ac_user_opts in *" "enable_$ac_useropt" "*) ;; *) ac_unrecognized_opts="$ac_unrecognized_opts$ac_unrecognized_sep--disable-$ac_useropt_orig" ac_unrecognized_sep=', ';; esac eval enable_$ac_useropt=no ;; -docdir | --docdir | --docdi | --doc | --do) ac_prev=docdir ;; -docdir=* | --docdir=* | --docdi=* | --doc=* | --do=*) docdir=$ac_optarg ;; -dvidir | --dvidir | --dvidi | --dvid | --dvi | --dv) ac_prev=dvidir ;; -dvidir=* | --dvidir=* | --dvidi=* | --dvid=* | --dvi=* | --dv=*) dvidir=$ac_optarg ;; -enable-* | --enable-*) ac_useropt=`expr "x$ac_option" : 'x-*enable-\([^=]*\)'` # Reject names that are not valid shell variable names. expr "x$ac_useropt" : ".*[^-+._$as_cr_alnum]" >/dev/null && as_fn_error $? "invalid feature name: \`$ac_useropt'" ac_useropt_orig=$ac_useropt ac_useropt=`printf "%s\n" "$ac_useropt" | sed 's/[-+.]/_/g'` case $ac_user_opts in *" "enable_$ac_useropt" "*) ;; *) ac_unrecognized_opts="$ac_unrecognized_opts$ac_unrecognized_sep--enable-$ac_useropt_orig" ac_unrecognized_sep=', ';; esac eval enable_$ac_useropt=\$ac_optarg ;; -exec-prefix | --exec_prefix | --exec-prefix | --exec-prefi \ | --exec-pref | --exec-pre | --exec-pr | --exec-p | --exec- \ | --exec | --exe | --ex) ac_prev=exec_prefix ;; -exec-prefix=* | --exec_prefix=* | --exec-prefix=* | --exec-prefi=* \ | --exec-pref=* | --exec-pre=* | --exec-pr=* | --exec-p=* | --exec-=* \ | --exec=* | --exe=* | --ex=*) exec_prefix=$ac_optarg ;; -gas | --gas | --ga | --g) # Obsolete; use --with-gas. with_gas=yes ;; -help | --help | --hel | --he | -h) ac_init_help=long ;; -help=r* | --help=r* | --hel=r* | --he=r* | -hr*) ac_init_help=recursive ;; -help=s* | --help=s* | --hel=s* | --he=s* | -hs*) ac_init_help=short ;; -host | --host | --hos | --ho) ac_prev=host_alias ;; -host=* | --host=* | --hos=* | --ho=*) host_alias=$ac_optarg ;; -htmldir | --htmldir | --htmldi | --htmld | --html | --htm | --ht) ac_prev=htmldir ;; -htmldir=* | --htmldir=* | --htmldi=* | --htmld=* | --html=* | --htm=* \ | --ht=*) htmldir=$ac_optarg ;; -includedir | --includedir | --includedi | --included | --include \ | --includ | --inclu | --incl | --inc) ac_prev=includedir ;; -includedir=* | --includedir=* | --includedi=* | --included=* | --include=* \ | --includ=* | --inclu=* | --incl=* | --inc=*) includedir=$ac_optarg ;; -infodir | --infodir | --infodi | --infod | --info | --inf) ac_prev=infodir ;; -infodir=* | --infodir=* | --infodi=* | --infod=* | --info=* | --inf=*) infodir=$ac_optarg ;; -libdir | --libdir | --libdi | --libd) ac_prev=libdir ;; -libdir=* | --libdir=* | --libdi=* | --libd=*) libdir=$ac_optarg ;; -libexecdir | --libexecdir | --libexecdi | --libexecd | --libexec \ | --libexe | --libex | --libe) ac_prev=libexecdir ;; -libexecdir=* | --libexecdir=* | --libexecdi=* | --libexecd=* | --libexec=* \ | --libexe=* | --libex=* | --libe=*) libexecdir=$ac_optarg ;; -localedir | --localedir | --localedi | --localed | --locale) ac_prev=localedir ;; -localedir=* | --localedir=* | --localedi=* | --localed=* | --locale=*) localedir=$ac_optarg ;; -localstatedir | --localstatedir | --localstatedi | --localstated \ | --localstate | --localstat | --localsta | --localst | --locals) ac_prev=localstatedir ;; -localstatedir=* | --localstatedir=* | --localstatedi=* | --localstated=* \ | --localstate=* | --localstat=* | --localsta=* | --localst=* | --locals=*) localstatedir=$ac_optarg ;; -mandir | --mandir | --mandi | --mand | --man | --ma | --m) ac_prev=mandir ;; -mandir=* | --mandir=* | --mandi=* | --mand=* | --man=* | --ma=* | --m=*) mandir=$ac_optarg ;; -nfp | --nfp | --nf) # Obsolete; use --without-fp. with_fp=no ;; -no-create | --no-create | --no-creat | --no-crea | --no-cre \ | --no-cr | --no-c | -n) no_create=yes ;; -no-recursion | --no-recursion | --no-recursio | --no-recursi \ | --no-recurs | --no-recur | --no-recu | --no-rec | --no-re | --no-r) no_recursion=yes ;; -oldincludedir | --oldincludedir | --oldincludedi | --oldincluded \ | --oldinclude | --oldinclud | --oldinclu | --oldincl | --oldinc \ | --oldin | --oldi | --old | --ol | --o) ac_prev=oldincludedir ;; -oldincludedir=* | --oldincludedir=* | --oldincludedi=* | --oldincluded=* \ | --oldinclude=* | --oldinclud=* | --oldinclu=* | --oldincl=* | --oldinc=* \ | --oldin=* | --oldi=* | --old=* | --ol=* | --o=*) oldincludedir=$ac_optarg ;; -prefix | --prefix | --prefi | --pref | --pre | --pr | --p) ac_prev=prefix ;; -prefix=* | --prefix=* | --prefi=* | --pref=* | --pre=* | --pr=* | --p=*) prefix=$ac_optarg ;; -program-prefix | --program-prefix | --program-prefi | --program-pref \ | --program-pre | --program-pr | --program-p) ac_prev=program_prefix ;; -program-prefix=* | --program-prefix=* | --program-prefi=* \ | --program-pref=* | --program-pre=* | --program-pr=* | --program-p=*) program_prefix=$ac_optarg ;; -program-suffix | --program-suffix | --program-suffi | --program-suff \ | --program-suf | --program-su | --program-s) ac_prev=program_suffix ;; -program-suffix=* | --program-suffix=* | --program-suffi=* \ | --program-suff=* | --program-suf=* | --program-su=* | --program-s=*) program_suffix=$ac_optarg ;; -program-transform-name | --program-transform-name \ | --program-transform-nam | --program-transform-na \ | --program-transform-n | --program-transform- \ | --program-transform | --program-transfor \ | --program-transfo | --program-transf \ | --program-trans | --program-tran \ | --progr-tra | --program-tr | --program-t) ac_prev=program_transform_name ;; -program-transform-name=* | --program-transform-name=* \ | --program-transform-nam=* | --program-transform-na=* \ | --program-transform-n=* | --program-transform-=* \ | --program-transform=* | --program-transfor=* \ | --program-transfo=* | --program-transf=* \ | --program-trans=* | --program-tran=* \ | --progr-tra=* | --program-tr=* | --program-t=*) program_transform_name=$ac_optarg ;; -pdfdir | --pdfdir | --pdfdi | --pdfd | --pdf | --pd) ac_prev=pdfdir ;; -pdfdir=* | --pdfdir=* | --pdfdi=* | --pdfd=* | --pdf=* | --pd=*) pdfdir=$ac_optarg ;; -psdir | --psdir | --psdi | --psd | --ps) ac_prev=psdir ;; -psdir=* | --psdir=* | --psdi=* | --psd=* | --ps=*) psdir=$ac_optarg ;; -q | -quiet | --quiet | --quie | --qui | --qu | --q \ | -silent | --silent | --silen | --sile | --sil) silent=yes ;; -runstatedir | --runstatedir | --runstatedi | --runstated \ | --runstate | --runstat | --runsta | --runst | --runs \ | --run | --ru | --r) ac_prev=runstatedir ;; -runstatedir=* | --runstatedir=* | --runstatedi=* | --runstated=* \ | --runstate=* | --runstat=* | --runsta=* | --runst=* | --runs=* \ | --run=* | --ru=* | --r=*) runstatedir=$ac_optarg ;; -sbindir | --sbindir | --sbindi | --sbind | --sbin | --sbi | --sb) ac_prev=sbindir ;; -sbindir=* | --sbindir=* | --sbindi=* | --sbind=* | --sbin=* \ | --sbi=* | --sb=*) sbindir=$ac_optarg ;; -sharedstatedir | --sharedstatedir | --sharedstatedi \ | --sharedstated | --sharedstate | --sharedstat | --sharedsta \ | --sharedst | --shareds | --shared | --share | --shar \ | --sha | --sh) ac_prev=sharedstatedir ;; -sharedstatedir=* | --sharedstatedir=* | --sharedstatedi=* \ | --sharedstated=* | --sharedstate=* | --sharedstat=* | --sharedsta=* \ | --sharedst=* | --shareds=* | --shared=* | --share=* | --shar=* \ | --sha=* | --sh=*) sharedstatedir=$ac_optarg ;; -site | --site | --sit) ac_prev=site ;; -site=* | --site=* | --sit=*) site=$ac_optarg ;; -srcdir | --srcdir | --srcdi | --srcd | --src | --sr) ac_prev=srcdir ;; -srcdir=* | --srcdir=* | --srcdi=* | --srcd=* | --src=* | --sr=*) srcdir=$ac_optarg ;; -sysconfdir | --sysconfdir | --sysconfdi | --sysconfd | --sysconf \ | --syscon | --sysco | --sysc | --sys | --sy) ac_prev=sysconfdir ;; -sysconfdir=* | --sysconfdir=* | --sysconfdi=* | --sysconfd=* | --sysconf=* \ | --syscon=* | --sysco=* | --sysc=* | --sys=* | --sy=*) sysconfdir=$ac_optarg ;; -target | --target | --targe | --targ | --tar | --ta | --t) ac_prev=target_alias ;; -target=* | --target=* | --targe=* | --targ=* | --tar=* | --ta=* | --t=*) target_alias=$ac_optarg ;; -v | -verbose | --verbose | --verbos | --verbo | --verb) verbose=yes ;; -version | --version | --versio | --versi | --vers | -V) ac_init_version=: ;; -with-* | --with-*) ac_useropt=`expr "x$ac_option" : 'x-*with-\([^=]*\)'` # Reject names that are not valid shell variable names. expr "x$ac_useropt" : ".*[^-+._$as_cr_alnum]" >/dev/null && as_fn_error $? "invalid package name: \`$ac_useropt'" ac_useropt_orig=$ac_useropt ac_useropt=`printf "%s\n" "$ac_useropt" | sed 's/[-+.]/_/g'` case $ac_user_opts in *" "with_$ac_useropt" "*) ;; *) ac_unrecognized_opts="$ac_unrecognized_opts$ac_unrecognized_sep--with-$ac_useropt_orig" ac_unrecognized_sep=', ';; esac eval with_$ac_useropt=\$ac_optarg ;; -without-* | --without-*) ac_useropt=`expr "x$ac_option" : 'x-*without-\(.*\)'` # Reject names that are not valid shell variable names. expr "x$ac_useropt" : ".*[^-+._$as_cr_alnum]" >/dev/null && as_fn_error $? "invalid package name: \`$ac_useropt'" ac_useropt_orig=$ac_useropt ac_useropt=`printf "%s\n" "$ac_useropt" | sed 's/[-+.]/_/g'` case $ac_user_opts in *" "with_$ac_useropt" "*) ;; *) ac_unrecognized_opts="$ac_unrecognized_opts$ac_unrecognized_sep--without-$ac_useropt_orig" ac_unrecognized_sep=', ';; esac eval with_$ac_useropt=no ;; --x) # Obsolete; use --with-x. with_x=yes ;; -x-includes | --x-includes | --x-include | --x-includ | --x-inclu \ | --x-incl | --x-inc | --x-in | --x-i) ac_prev=x_includes ;; -x-includes=* | --x-includes=* | --x-include=* | --x-includ=* | --x-inclu=* \ | --x-incl=* | --x-inc=* | --x-in=* | --x-i=*) x_includes=$ac_optarg ;; -x-libraries | --x-libraries | --x-librarie | --x-librari \ | --x-librar | --x-libra | --x-libr | --x-lib | --x-li | --x-l) ac_prev=x_libraries ;; -x-libraries=* | --x-libraries=* | --x-librarie=* | --x-librari=* \ | --x-librar=* | --x-libra=* | --x-libr=* | --x-lib=* | --x-li=* | --x-l=*) x_libraries=$ac_optarg ;; -*) as_fn_error $? "unrecognized option: \`$ac_option' Try \`$0 --help' for more information" ;; *=*) ac_envvar=`expr "x$ac_option" : 'x\([^=]*\)='` # Reject names that are not valid shell variable names. case $ac_envvar in #( '' | [0-9]* | *[!_$as_cr_alnum]* ) as_fn_error $? "invalid variable name: \`$ac_envvar'" ;; esac eval $ac_envvar=\$ac_optarg export $ac_envvar ;; *) # FIXME: should be removed in autoconf 3.0. printf "%s\n" "$as_me: WARNING: you should use --build, --host, --target" >&2 expr "x$ac_option" : ".*[^-._$as_cr_alnum]" >/dev/null && printf "%s\n" "$as_me: WARNING: invalid host type: $ac_option" >&2 : "${build_alias=$ac_option} ${host_alias=$ac_option} ${target_alias=$ac_option}" ;; esac done if test -n "$ac_prev"; then ac_option=--`echo $ac_prev | sed 's/_/-/g'` as_fn_error $? "missing argument to $ac_option" fi if test -n "$ac_unrecognized_opts"; then case $enable_option_checking in no) ;; fatal) as_fn_error $? "unrecognized options: $ac_unrecognized_opts" ;; *) printf "%s\n" "$as_me: WARNING: unrecognized options: $ac_unrecognized_opts" >&2 ;; esac fi # Check all directory arguments for consistency. for ac_var in exec_prefix prefix bindir sbindir libexecdir datarootdir \ datadir sysconfdir sharedstatedir localstatedir includedir \ oldincludedir docdir infodir htmldir dvidir pdfdir psdir \ libdir localedir mandir runstatedir do eval ac_val=\$$ac_var # Remove trailing slashes. case $ac_val in */ ) ac_val=`expr "X$ac_val" : 'X\(.*[^/]\)' \| "X$ac_val" : 'X\(.*\)'` eval $ac_var=\$ac_val;; esac # Be sure to have absolute directory names. case $ac_val in [\\/$]* | ?:[\\/]* ) continue;; NONE | '' ) case $ac_var in *prefix ) continue;; esac;; esac as_fn_error $? "expected an absolute directory name for --$ac_var: $ac_val" done # There might be people who depend on the old broken behavior: `$host' # used to hold the argument of --host etc. # FIXME: To remove some day. build=$build_alias host=$host_alias target=$target_alias # FIXME: To remove some day. if test "x$host_alias" != x; then if test "x$build_alias" = x; then cross_compiling=maybe elif test "x$build_alias" != "x$host_alias"; then cross_compiling=yes fi fi ac_tool_prefix= test -n "$host_alias" && ac_tool_prefix=$host_alias- test "$silent" = yes && exec 6>/dev/null ac_pwd=`pwd` && test -n "$ac_pwd" && ac_ls_di=`ls -di .` && ac_pwd_ls_di=`cd "$ac_pwd" && ls -di .` || as_fn_error $? "working directory cannot be determined" test "X$ac_ls_di" = "X$ac_pwd_ls_di" || as_fn_error $? "pwd does not report name of working directory" # Find the source files, if location was not specified. if test -z "$srcdir"; then ac_srcdir_defaulted=yes # Try the directory containing this script, then the parent directory. ac_confdir=`$as_dirname -- "$as_myself" || $as_expr X"$as_myself" : 'X\(.*[^/]\)//*[^/][^/]*/*$' \| \ X"$as_myself" : 'X\(//\)[^/]' \| \ X"$as_myself" : 'X\(//\)$' \| \ X"$as_myself" : 'X\(/\)' \| . 2>/dev/null || printf "%s\n" X"$as_myself" | sed '/^X\(.*[^/]\)\/\/*[^/][^/]*\/*$/{ s//\1/ q } /^X\(\/\/\)[^/].*/{ s//\1/ q } /^X\(\/\/\)$/{ s//\1/ q } /^X\(\/\).*/{ s//\1/ q } s/.*/./; q'` srcdir=$ac_confdir if test ! -r "$srcdir/$ac_unique_file"; then srcdir=.. fi else ac_srcdir_defaulted=no fi if test ! -r "$srcdir/$ac_unique_file"; then test "$ac_srcdir_defaulted" = yes && srcdir="$ac_confdir or .." as_fn_error $? "cannot find sources ($ac_unique_file) in $srcdir" fi ac_msg="sources are in $srcdir, but \`cd $srcdir' does not work" ac_abs_confdir=`( cd "$srcdir" && test -r "./$ac_unique_file" || as_fn_error $? "$ac_msg" pwd)` # When building in place, set srcdir=. if test "$ac_abs_confdir" = "$ac_pwd"; then srcdir=. fi # Remove unnecessary trailing slashes from srcdir. # Double slashes in file names in object file debugging info # mess up M-x gdb in Emacs. case $srcdir in */) srcdir=`expr "X$srcdir" : 'X\(.*[^/]\)' \| "X$srcdir" : 'X\(.*\)'`;; esac for ac_var in $ac_precious_vars; do eval ac_env_${ac_var}_set=\${${ac_var}+set} eval ac_env_${ac_var}_value=\$${ac_var} eval ac_cv_env_${ac_var}_set=\${${ac_var}+set} eval ac_cv_env_${ac_var}_value=\$${ac_var} done # # Report the --help message. # if test "$ac_init_help" = "long"; then # Omit some internal or obsolete options to make the list less imposing. # This message is too long to be a string in the A/UX 3.1 sh. cat <<_ACEOF \`configure' configures this package to adapt to many kinds of systems. Usage: $0 [OPTION]... [VAR=VALUE]... To assign environment variables (e.g., CC, CFLAGS...), specify them as VAR=VALUE. See below for descriptions of some of the useful variables. Defaults for the options are specified in brackets. Configuration: -h, --help display this help and exit --help=short display options specific to this package --help=recursive display the short help of all the included packages -V, --version display version information and exit -q, --quiet, --silent do not print \`checking ...' messages --cache-file=FILE cache test results in FILE [disabled] -C, --config-cache alias for \`--cache-file=config.cache' -n, --no-create do not create output files --srcdir=DIR find the sources in DIR [configure dir or \`..'] Installation directories: --prefix=PREFIX install architecture-independent files in PREFIX [$ac_default_prefix] --exec-prefix=EPREFIX install architecture-dependent files in EPREFIX [PREFIX] By default, \`make install' will install all the files in \`$ac_default_prefix/bin', \`$ac_default_prefix/lib' etc. You can specify an installation prefix other than \`$ac_default_prefix' using \`--prefix', for instance \`--prefix=\$HOME'. For better control, use the options below. Fine tuning of the installation directories: --bindir=DIR user executables [EPREFIX/bin] --sbindir=DIR system admin executables [EPREFIX/sbin] --libexecdir=DIR program executables [EPREFIX/libexec] --sysconfdir=DIR read-only single-machine data [PREFIX/etc] --sharedstatedir=DIR modifiable architecture-independent data [PREFIX/com] --localstatedir=DIR modifiable single-machine data [PREFIX/var] --runstatedir=DIR modifiable per-process data [LOCALSTATEDIR/run] --libdir=DIR object code libraries [EPREFIX/lib] --includedir=DIR C header files [PREFIX/include] --oldincludedir=DIR C header files for non-gcc [/usr/include] --datarootdir=DIR read-only arch.-independent data root [PREFIX/share] --datadir=DIR read-only architecture-independent data [DATAROOTDIR] --infodir=DIR info documentation [DATAROOTDIR/info] --localedir=DIR locale-dependent data [DATAROOTDIR/locale] --mandir=DIR man documentation [DATAROOTDIR/man] --docdir=DIR documentation root [DATAROOTDIR/doc/PACKAGE] --htmldir=DIR html documentation [DOCDIR] --dvidir=DIR dvi documentation [DOCDIR] --pdfdir=DIR pdf documentation [DOCDIR] --psdir=DIR ps documentation [DOCDIR] _ACEOF cat <<\_ACEOF _ACEOF fi if test -n "$ac_init_help"; then cat <<\_ACEOF Some influential environment variables: CC C compiler command CFLAGS C compiler flags LDFLAGS linker flags, e.g. -L if you have libraries in a nonstandard directory LIBS libraries to pass to the linker, e.g. -l CPPFLAGS (Objective) C/C++ preprocessor flags, e.g. -I if you have headers in a nonstandard directory Use these variables to override the choices made by `configure' or to help it to find libraries and programs with nonstandard names/locations. Report bugs to the package provider. _ACEOF ac_status=$? fi if test "$ac_init_help" = "recursive"; then # If there are subdirs, report their specific --help. for ac_dir in : $ac_subdirs_all; do test "x$ac_dir" = x: && continue test -d "$ac_dir" || { cd "$srcdir" && ac_pwd=`pwd` && srcdir=. && test -d "$ac_dir"; } || continue ac_builddir=. case "$ac_dir" in .) ac_dir_suffix= ac_top_builddir_sub=. ac_top_build_prefix= ;; *) ac_dir_suffix=/`printf "%s\n" "$ac_dir" | sed 's|^\.[\\/]||'` # A ".." for each directory in $ac_dir_suffix. ac_top_builddir_sub=`printf "%s\n" "$ac_dir_suffix" | sed 's|/[^\\/]*|/..|g;s|/||'` case $ac_top_builddir_sub in "") ac_top_builddir_sub=. ac_top_build_prefix= ;; *) ac_top_build_prefix=$ac_top_builddir_sub/ ;; esac ;; esac ac_abs_top_builddir=$ac_pwd ac_abs_builddir=$ac_pwd$ac_dir_suffix # for backward compatibility: ac_top_builddir=$ac_top_build_prefix case $srcdir in .) # We are building in place. ac_srcdir=. ac_top_srcdir=$ac_top_builddir_sub ac_abs_top_srcdir=$ac_pwd ;; [\\/]* | ?:[\\/]* ) # Absolute name. ac_srcdir=$srcdir$ac_dir_suffix; ac_top_srcdir=$srcdir ac_abs_top_srcdir=$srcdir ;; *) # Relative name. ac_srcdir=$ac_top_build_prefix$srcdir$ac_dir_suffix ac_top_srcdir=$ac_top_build_prefix$srcdir ac_abs_top_srcdir=$ac_pwd/$srcdir ;; esac ac_abs_srcdir=$ac_abs_top_srcdir$ac_dir_suffix cd "$ac_dir" || { ac_status=$?; continue; } # Check for configure.gnu first; this name is used for a wrapper for # Metaconfig's "Configure" on case-insensitive file systems. if test -f "$ac_srcdir/configure.gnu"; then echo && $SHELL "$ac_srcdir/configure.gnu" --help=recursive elif test -f "$ac_srcdir/configure"; then echo && $SHELL "$ac_srcdir/configure" --help=recursive else printf "%s\n" "$as_me: WARNING: no configuration information is in $ac_dir" >&2 fi || ac_status=$? cd "$ac_pwd" || { ac_status=$?; break; } done fi test -n "$ac_init_help" && exit $ac_status if $ac_init_version; then cat <<\_ACEOF configure generated by GNU Autoconf 2.71 Copyright (C) 2021 Free Software Foundation, Inc. This configure script is free software; the Free Software Foundation gives unlimited permission to copy, distribute and modify it. _ACEOF exit fi ## ------------------------ ## ## Autoconf initialization. ## ## ------------------------ ## # ac_fn_c_try_compile LINENO # -------------------------- # Try to compile conftest.$ac_ext, and return whether this succeeded. ac_fn_c_try_compile () { as_lineno=${as_lineno-"$1"} as_lineno_stack=as_lineno_stack=$as_lineno_stack rm -f conftest.$ac_objext conftest.beam if { { ac_try="$ac_compile" case "(($ac_try" in *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; *) ac_try_echo=$ac_try;; esac eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\"" printf "%s\n" "$ac_try_echo"; } >&5 (eval "$ac_compile") 2>conftest.err ac_status=$? if test -s conftest.err; then grep -v '^ *+' conftest.err >conftest.er1 cat conftest.er1 >&5 mv -f conftest.er1 conftest.err fi printf "%s\n" "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 test $ac_status = 0; } && { test -z "$ac_c_werror_flag" || test ! -s conftest.err } && test -s conftest.$ac_objext then : ac_retval=0 else $as_nop printf "%s\n" "$as_me: failed program was:" >&5 sed 's/^/| /' conftest.$ac_ext >&5 ac_retval=1 fi eval $as_lineno_stack; ${as_lineno_stack:+:} unset as_lineno as_fn_set_status $ac_retval } # ac_fn_c_try_compile # ac_fn_c_check_header_compile LINENO HEADER VAR INCLUDES # ------------------------------------------------------- # Tests whether HEADER exists and can be compiled using the include files in # INCLUDES, setting the cache variable VAR accordingly. ac_fn_c_check_header_compile () { as_lineno=${as_lineno-"$1"} as_lineno_stack=as_lineno_stack=$as_lineno_stack { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for $2" >&5 printf %s "checking for $2... " >&6; } if eval test \${$3+y} then : printf %s "(cached) " >&6 else $as_nop cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ $4 #include <$2> _ACEOF if ac_fn_c_try_compile "$LINENO" then : eval "$3=yes" else $as_nop eval "$3=no" fi rm -f core conftest.err conftest.$ac_objext conftest.beam conftest.$ac_ext fi eval ac_res=\$$3 { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_res" >&5 printf "%s\n" "$ac_res" >&6; } eval $as_lineno_stack; ${as_lineno_stack:+:} unset as_lineno } # ac_fn_c_check_header_compile ac_configure_args_raw= for ac_arg do case $ac_arg in *\'*) ac_arg=`printf "%s\n" "$ac_arg" | sed "s/'/'\\\\\\\\''/g"` ;; esac as_fn_append ac_configure_args_raw " '$ac_arg'" done case $ac_configure_args_raw in *$as_nl*) ac_safe_unquote= ;; *) ac_unsafe_z='|&;<>()$`\\"*?[ '' ' # This string ends in space, tab. ac_unsafe_a="$ac_unsafe_z#~" ac_safe_unquote="s/ '\\([^$ac_unsafe_a][^$ac_unsafe_z]*\\)'/ \\1/g" ac_configure_args_raw=` printf "%s\n" "$ac_configure_args_raw" | sed "$ac_safe_unquote"`;; esac cat >config.log <<_ACEOF This file contains any messages produced by compilers while running configure, to aid debugging if configure makes a mistake. It was created by $as_me, which was generated by GNU Autoconf 2.71. Invocation command line was $ $0$ac_configure_args_raw _ACEOF exec 5>>config.log { cat <<_ASUNAME ## --------- ## ## Platform. ## ## --------- ## hostname = `(hostname || uname -n) 2>/dev/null | sed 1q` uname -m = `(uname -m) 2>/dev/null || echo unknown` uname -r = `(uname -r) 2>/dev/null || echo unknown` uname -s = `(uname -s) 2>/dev/null || echo unknown` uname -v = `(uname -v) 2>/dev/null || echo unknown` /usr/bin/uname -p = `(/usr/bin/uname -p) 2>/dev/null || echo unknown` /bin/uname -X = `(/bin/uname -X) 2>/dev/null || echo unknown` /bin/arch = `(/bin/arch) 2>/dev/null || echo unknown` /usr/bin/arch -k = `(/usr/bin/arch -k) 2>/dev/null || echo unknown` /usr/convex/getsysinfo = `(/usr/convex/getsysinfo) 2>/dev/null || echo unknown` /usr/bin/hostinfo = `(/usr/bin/hostinfo) 2>/dev/null || echo unknown` /bin/machine = `(/bin/machine) 2>/dev/null || echo unknown` /usr/bin/oslevel = `(/usr/bin/oslevel) 2>/dev/null || echo unknown` /bin/universe = `(/bin/universe) 2>/dev/null || echo unknown` _ASUNAME as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS case $as_dir in #((( '') as_dir=./ ;; */) ;; *) as_dir=$as_dir/ ;; esac printf "%s\n" "PATH: $as_dir" done IFS=$as_save_IFS } >&5 cat >&5 <<_ACEOF ## ----------- ## ## Core tests. ## ## ----------- ## _ACEOF # Keep a trace of the command line. # Strip out --no-create and --no-recursion so they do not pile up. # Strip out --silent because we don't want to record it for future runs. # Also quote any args containing shell meta-characters. # Make two passes to allow for proper duplicate-argument suppression. ac_configure_args= ac_configure_args0= ac_configure_args1= ac_must_keep_next=false for ac_pass in 1 2 do for ac_arg do case $ac_arg in -no-create | --no-c* | -n | -no-recursion | --no-r*) continue ;; -q | -quiet | --quiet | --quie | --qui | --qu | --q \ | -silent | --silent | --silen | --sile | --sil) continue ;; *\'*) ac_arg=`printf "%s\n" "$ac_arg" | sed "s/'/'\\\\\\\\''/g"` ;; esac case $ac_pass in 1) as_fn_append ac_configure_args0 " '$ac_arg'" ;; 2) as_fn_append ac_configure_args1 " '$ac_arg'" if test $ac_must_keep_next = true; then ac_must_keep_next=false # Got value, back to normal. else case $ac_arg in *=* | --config-cache | -C | -disable-* | --disable-* \ | -enable-* | --enable-* | -gas | --g* | -nfp | --nf* \ | -q | -quiet | --q* | -silent | --sil* | -v | -verb* \ | -with-* | --with-* | -without-* | --without-* | --x) case "$ac_configure_args0 " in "$ac_configure_args1"*" '$ac_arg' "* ) continue ;; esac ;; -* ) ac_must_keep_next=true ;; esac fi as_fn_append ac_configure_args " '$ac_arg'" ;; esac done done { ac_configure_args0=; unset ac_configure_args0;} { ac_configure_args1=; unset ac_configure_args1;} # When interrupted or exit'd, cleanup temporary files, and complete # config.log. We remove comments because anyway the quotes in there # would cause problems or look ugly. # WARNING: Use '\'' to represent an apostrophe within the trap. # WARNING: Do not start the trap code with a newline, due to a FreeBSD 4.0 bug. trap 'exit_status=$? # Sanitize IFS. IFS=" "" $as_nl" # Save into config.log some information that might help in debugging. { echo printf "%s\n" "## ---------------- ## ## Cache variables. ## ## ---------------- ##" echo # The following way of writing the cache mishandles newlines in values, ( for ac_var in `(set) 2>&1 | sed -n '\''s/^\([a-zA-Z_][a-zA-Z0-9_]*\)=.*/\1/p'\''`; do eval ac_val=\$$ac_var case $ac_val in #( *${as_nl}*) case $ac_var in #( *_cv_*) { printf "%s\n" "$as_me:${as_lineno-$LINENO}: WARNING: cache variable $ac_var contains a newline" >&5 printf "%s\n" "$as_me: WARNING: cache variable $ac_var contains a newline" >&2;} ;; esac case $ac_var in #( _ | IFS | as_nl) ;; #( BASH_ARGV | BASH_SOURCE) eval $ac_var= ;; #( *) { eval $ac_var=; unset $ac_var;} ;; esac ;; esac done (set) 2>&1 | case $as_nl`(ac_space='\'' '\''; set) 2>&1` in #( *${as_nl}ac_space=\ *) sed -n \ "s/'\''/'\''\\\\'\'''\''/g; s/^\\([_$as_cr_alnum]*_cv_[_$as_cr_alnum]*\\)=\\(.*\\)/\\1='\''\\2'\''/p" ;; #( *) sed -n "/^[_$as_cr_alnum]*_cv_[_$as_cr_alnum]*=/p" ;; esac | sort ) echo printf "%s\n" "## ----------------- ## ## Output variables. ## ## ----------------- ##" echo for ac_var in $ac_subst_vars do eval ac_val=\$$ac_var case $ac_val in *\'\''*) ac_val=`printf "%s\n" "$ac_val" | sed "s/'\''/'\''\\\\\\\\'\'''\''/g"`;; esac printf "%s\n" "$ac_var='\''$ac_val'\''" done | sort echo if test -n "$ac_subst_files"; then printf "%s\n" "## ------------------- ## ## File substitutions. ## ## ------------------- ##" echo for ac_var in $ac_subst_files do eval ac_val=\$$ac_var case $ac_val in *\'\''*) ac_val=`printf "%s\n" "$ac_val" | sed "s/'\''/'\''\\\\\\\\'\'''\''/g"`;; esac printf "%s\n" "$ac_var='\''$ac_val'\''" done | sort echo fi if test -s confdefs.h; then printf "%s\n" "## ----------- ## ## confdefs.h. ## ## ----------- ##" echo cat confdefs.h echo fi test "$ac_signal" != 0 && printf "%s\n" "$as_me: caught signal $ac_signal" printf "%s\n" "$as_me: exit $exit_status" } >&5 rm -f core *.core core.conftest.* && rm -f -r conftest* confdefs* conf$$* $ac_clean_files && exit $exit_status ' 0 for ac_signal in 1 2 13 15; do trap 'ac_signal='$ac_signal'; as_fn_exit 1' $ac_signal done ac_signal=0 # confdefs.h avoids OS command line length limits that DEFS can exceed. rm -f -r conftest* confdefs.h printf "%s\n" "/* confdefs.h */" > confdefs.h # Predefined preprocessor variables. printf "%s\n" "#define PACKAGE_NAME \"$PACKAGE_NAME\"" >>confdefs.h printf "%s\n" "#define PACKAGE_TARNAME \"$PACKAGE_TARNAME\"" >>confdefs.h printf "%s\n" "#define PACKAGE_VERSION \"$PACKAGE_VERSION\"" >>confdefs.h printf "%s\n" "#define PACKAGE_STRING \"$PACKAGE_STRING\"" >>confdefs.h printf "%s\n" "#define PACKAGE_BUGREPORT \"$PACKAGE_BUGREPORT\"" >>confdefs.h printf "%s\n" "#define PACKAGE_URL \"$PACKAGE_URL\"" >>confdefs.h # Let the site file select an alternate cache file if it wants to. # Prefer an explicitly selected file to automatically selected ones. if test -n "$CONFIG_SITE"; then ac_site_files="$CONFIG_SITE" elif test "x$prefix" != xNONE; then ac_site_files="$prefix/share/config.site $prefix/etc/config.site" else ac_site_files="$ac_default_prefix/share/config.site $ac_default_prefix/etc/config.site" fi for ac_site_file in $ac_site_files do case $ac_site_file in #( */*) : ;; #( *) : ac_site_file=./$ac_site_file ;; esac if test -f "$ac_site_file" && test -r "$ac_site_file"; then { printf "%s\n" "$as_me:${as_lineno-$LINENO}: loading site script $ac_site_file" >&5 printf "%s\n" "$as_me: loading site script $ac_site_file" >&6;} sed 's/^/| /' "$ac_site_file" >&5 . "$ac_site_file" \ || { { printf "%s\n" "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5 printf "%s\n" "$as_me: error: in \`$ac_pwd':" >&2;} as_fn_error $? "failed to load site script $ac_site_file See \`config.log' for more details" "$LINENO" 5; } fi done if test -r "$cache_file"; then # Some versions of bash will fail to source /dev/null (special files # actually), so we avoid doing that. DJGPP emulates it as a regular file. if test /dev/null != "$cache_file" && test -f "$cache_file"; then { printf "%s\n" "$as_me:${as_lineno-$LINENO}: loading cache $cache_file" >&5 printf "%s\n" "$as_me: loading cache $cache_file" >&6;} case $cache_file in [\\/]* | ?:[\\/]* ) . "$cache_file";; *) . "./$cache_file";; esac fi else { printf "%s\n" "$as_me:${as_lineno-$LINENO}: creating cache $cache_file" >&5 printf "%s\n" "$as_me: creating cache $cache_file" >&6;} >$cache_file fi # Test code for whether the C compiler supports C89 (global declarations) ac_c_conftest_c89_globals=' /* Does the compiler advertise C89 conformance? Do not test the value of __STDC__, because some compilers set it to 0 while being otherwise adequately conformant. */ #if !defined __STDC__ # error "Compiler does not advertise C89 conformance" #endif #include #include struct stat; /* Most of the following tests are stolen from RCS 5.7 src/conf.sh. */ struct buf { int x; }; struct buf * (*rcsopen) (struct buf *, struct stat *, int); static char *e (p, i) char **p; int i; { return p[i]; } static char *f (char * (*g) (char **, int), char **p, ...) { char *s; va_list v; va_start (v,p); s = g (p, va_arg (v,int)); va_end (v); return s; } /* OSF 4.0 Compaq cc is some sort of almost-ANSI by default. It has function prototypes and stuff, but not \xHH hex character constants. These do not provoke an error unfortunately, instead are silently treated as an "x". The following induces an error, until -std is added to get proper ANSI mode. Curiously \x00 != x always comes out true, for an array size at least. It is necessary to write \x00 == 0 to get something that is true only with -std. */ int osf4_cc_array ['\''\x00'\'' == 0 ? 1 : -1]; /* IBM C 6 for AIX is almost-ANSI by default, but it replaces macro parameters inside strings and character constants. */ #define FOO(x) '\''x'\'' int xlc6_cc_array[FOO(a) == '\''x'\'' ? 1 : -1]; int test (int i, double x); struct s1 {int (*f) (int a);}; struct s2 {int (*f) (double a);}; int pairnames (int, char **, int *(*)(struct buf *, struct stat *, int), int, int);' # Test code for whether the C compiler supports C89 (body of main). ac_c_conftest_c89_main=' ok |= (argc == 0 || f (e, argv, 0) != argv[0] || f (e, argv, 1) != argv[1]); ' # Test code for whether the C compiler supports C99 (global declarations) ac_c_conftest_c99_globals=' // Does the compiler advertise C99 conformance? #if !defined __STDC_VERSION__ || __STDC_VERSION__ < 199901L # error "Compiler does not advertise C99 conformance" #endif #include extern int puts (const char *); extern int printf (const char *, ...); extern int dprintf (int, const char *, ...); extern void *malloc (size_t); // Check varargs macros. These examples are taken from C99 6.10.3.5. // dprintf is used instead of fprintf to avoid needing to declare // FILE and stderr. #define debug(...) dprintf (2, __VA_ARGS__) #define showlist(...) puts (#__VA_ARGS__) #define report(test,...) ((test) ? puts (#test) : printf (__VA_ARGS__)) static void test_varargs_macros (void) { int x = 1234; int y = 5678; debug ("Flag"); debug ("X = %d\n", x); showlist (The first, second, and third items.); report (x>y, "x is %d but y is %d", x, y); } // Check long long types. #define BIG64 18446744073709551615ull #define BIG32 4294967295ul #define BIG_OK (BIG64 / BIG32 == 4294967297ull && BIG64 % BIG32 == 0) #if !BIG_OK #error "your preprocessor is broken" #endif #if BIG_OK #else #error "your preprocessor is broken" #endif static long long int bignum = -9223372036854775807LL; static unsigned long long int ubignum = BIG64; struct incomplete_array { int datasize; double data[]; }; struct named_init { int number; const wchar_t *name; double average; }; typedef const char *ccp; static inline int test_restrict (ccp restrict text) { // See if C++-style comments work. // Iterate through items via the restricted pointer. // Also check for declarations in for loops. for (unsigned int i = 0; *(text+i) != '\''\0'\''; ++i) continue; return 0; } // Check varargs and va_copy. static bool test_varargs (const char *format, ...) { va_list args; va_start (args, format); va_list args_copy; va_copy (args_copy, args); const char *str = ""; int number = 0; float fnumber = 0; while (*format) { switch (*format++) { case '\''s'\'': // string str = va_arg (args_copy, const char *); break; case '\''d'\'': // int number = va_arg (args_copy, int); break; case '\''f'\'': // float fnumber = va_arg (args_copy, double); break; default: break; } } va_end (args_copy); va_end (args); return *str && number && fnumber; } ' # Test code for whether the C compiler supports C99 (body of main). ac_c_conftest_c99_main=' // Check bool. _Bool success = false; success |= (argc != 0); // Check restrict. if (test_restrict ("String literal") == 0) success = true; char *restrict newvar = "Another string"; // Check varargs. success &= test_varargs ("s, d'\'' f .", "string", 65, 34.234); test_varargs_macros (); // Check flexible array members. struct incomplete_array *ia = malloc (sizeof (struct incomplete_array) + (sizeof (double) * 10)); ia->datasize = 10; for (int i = 0; i < ia->datasize; ++i) ia->data[i] = i * 1.234; // Check named initializers. struct named_init ni = { .number = 34, .name = L"Test wide string", .average = 543.34343, }; ni.number = 58; int dynamic_array[ni.number]; dynamic_array[0] = argv[0][0]; dynamic_array[ni.number - 1] = 543; // work around unused variable warnings ok |= (!success || bignum == 0LL || ubignum == 0uLL || newvar[0] == '\''x'\'' || dynamic_array[ni.number - 1] != 543); ' # Test code for whether the C compiler supports C11 (global declarations) ac_c_conftest_c11_globals=' // Does the compiler advertise C11 conformance? #if !defined __STDC_VERSION__ || __STDC_VERSION__ < 201112L # error "Compiler does not advertise C11 conformance" #endif // Check _Alignas. char _Alignas (double) aligned_as_double; char _Alignas (0) no_special_alignment; extern char aligned_as_int; char _Alignas (0) _Alignas (int) aligned_as_int; // Check _Alignof. enum { int_alignment = _Alignof (int), int_array_alignment = _Alignof (int[100]), char_alignment = _Alignof (char) }; _Static_assert (0 < -_Alignof (int), "_Alignof is signed"); // Check _Noreturn. int _Noreturn does_not_return (void) { for (;;) continue; } // Check _Static_assert. struct test_static_assert { int x; _Static_assert (sizeof (int) <= sizeof (long int), "_Static_assert does not work in struct"); long int y; }; // Check UTF-8 literals. #define u8 syntax error! char const utf8_literal[] = u8"happens to be ASCII" "another string"; // Check duplicate typedefs. typedef long *long_ptr; typedef long int *long_ptr; typedef long_ptr long_ptr; // Anonymous structures and unions -- taken from C11 6.7.2.1 Example 1. struct anonymous { union { struct { int i; int j; }; struct { int k; long int l; } w; }; int m; } v1; ' # Test code for whether the C compiler supports C11 (body of main). ac_c_conftest_c11_main=' _Static_assert ((offsetof (struct anonymous, i) == offsetof (struct anonymous, w.k)), "Anonymous union alignment botch"); v1.i = 2; v1.w.k = 5; ok |= v1.i != 5; ' # Test code for whether the C compiler supports C11 (complete). ac_c_conftest_c11_program="${ac_c_conftest_c89_globals} ${ac_c_conftest_c99_globals} ${ac_c_conftest_c11_globals} int main (int argc, char **argv) { int ok = 0; ${ac_c_conftest_c89_main} ${ac_c_conftest_c99_main} ${ac_c_conftest_c11_main} return ok; } " # Test code for whether the C compiler supports C99 (complete). ac_c_conftest_c99_program="${ac_c_conftest_c89_globals} ${ac_c_conftest_c99_globals} int main (int argc, char **argv) { int ok = 0; ${ac_c_conftest_c89_main} ${ac_c_conftest_c99_main} return ok; } " # Test code for whether the C compiler supports C89 (complete). ac_c_conftest_c89_program="${ac_c_conftest_c89_globals} int main (int argc, char **argv) { int ok = 0; ${ac_c_conftest_c89_main} return ok; } " as_fn_append ac_header_c_list " stdio.h stdio_h HAVE_STDIO_H" as_fn_append ac_header_c_list " stdlib.h stdlib_h HAVE_STDLIB_H" as_fn_append ac_header_c_list " string.h string_h HAVE_STRING_H" as_fn_append ac_header_c_list " inttypes.h inttypes_h HAVE_INTTYPES_H" as_fn_append ac_header_c_list " stdint.h stdint_h HAVE_STDINT_H" as_fn_append ac_header_c_list " strings.h strings_h HAVE_STRINGS_H" as_fn_append ac_header_c_list " sys/stat.h sys_stat_h HAVE_SYS_STAT_H" as_fn_append ac_header_c_list " sys/types.h sys_types_h HAVE_SYS_TYPES_H" as_fn_append ac_header_c_list " unistd.h unistd_h HAVE_UNISTD_H" # Check that the precious variables saved in the cache have kept the same # value. ac_cache_corrupted=false for ac_var in $ac_precious_vars; do eval ac_old_set=\$ac_cv_env_${ac_var}_set eval ac_new_set=\$ac_env_${ac_var}_set eval ac_old_val=\$ac_cv_env_${ac_var}_value eval ac_new_val=\$ac_env_${ac_var}_value case $ac_old_set,$ac_new_set in set,) { printf "%s\n" "$as_me:${as_lineno-$LINENO}: error: \`$ac_var' was set to \`$ac_old_val' in the previous run" >&5 printf "%s\n" "$as_me: error: \`$ac_var' was set to \`$ac_old_val' in the previous run" >&2;} ac_cache_corrupted=: ;; ,set) { printf "%s\n" "$as_me:${as_lineno-$LINENO}: error: \`$ac_var' was not set in the previous run" >&5 printf "%s\n" "$as_me: error: \`$ac_var' was not set in the previous run" >&2;} ac_cache_corrupted=: ;; ,);; *) if test "x$ac_old_val" != "x$ac_new_val"; then # differences in whitespace do not lead to failure. ac_old_val_w=`echo x $ac_old_val` ac_new_val_w=`echo x $ac_new_val` if test "$ac_old_val_w" != "$ac_new_val_w"; then { printf "%s\n" "$as_me:${as_lineno-$LINENO}: error: \`$ac_var' has changed since the previous run:" >&5 printf "%s\n" "$as_me: error: \`$ac_var' has changed since the previous run:" >&2;} ac_cache_corrupted=: else { printf "%s\n" "$as_me:${as_lineno-$LINENO}: warning: ignoring whitespace changes in \`$ac_var' since the previous run:" >&5 printf "%s\n" "$as_me: warning: ignoring whitespace changes in \`$ac_var' since the previous run:" >&2;} eval $ac_var=\$ac_old_val fi { printf "%s\n" "$as_me:${as_lineno-$LINENO}: former value: \`$ac_old_val'" >&5 printf "%s\n" "$as_me: former value: \`$ac_old_val'" >&2;} { printf "%s\n" "$as_me:${as_lineno-$LINENO}: current value: \`$ac_new_val'" >&5 printf "%s\n" "$as_me: current value: \`$ac_new_val'" >&2;} fi;; esac # Pass precious variables to config.status. if test "$ac_new_set" = set; then case $ac_new_val in *\'*) ac_arg=$ac_var=`printf "%s\n" "$ac_new_val" | sed "s/'/'\\\\\\\\''/g"` ;; *) ac_arg=$ac_var=$ac_new_val ;; esac case " $ac_configure_args " in *" '$ac_arg' "*) ;; # Avoid dups. Use of quotes ensures accuracy. *) as_fn_append ac_configure_args " '$ac_arg'" ;; esac fi done if $ac_cache_corrupted; then { printf "%s\n" "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5 printf "%s\n" "$as_me: error: in \`$ac_pwd':" >&2;} { printf "%s\n" "$as_me:${as_lineno-$LINENO}: error: changes in the environment can compromise the build" >&5 printf "%s\n" "$as_me: error: changes in the environment can compromise the build" >&2;} as_fn_error $? "run \`${MAKE-make} distclean' and/or \`rm $cache_file' and start over" "$LINENO" 5 fi ## -------------------- ## ## Main body of script. ## ## -------------------- ## ac_ext=c ac_cpp='$CPP $CPPFLAGS' ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5' ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' ac_compiler_gnu=$ac_cv_c_compiler_gnu ac_config_headers="$ac_config_headers config.h" ac_config_files="$ac_config_files Makefile" ac_ext=c ac_cpp='$CPP $CPPFLAGS' ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5' ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' ac_compiler_gnu=$ac_cv_c_compiler_gnu if test -n "$ac_tool_prefix"; then # Extract the first word of "${ac_tool_prefix}gcc", so it can be a program name with args. set dummy ${ac_tool_prefix}gcc; ac_word=$2 { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 printf %s "checking for $ac_word... " >&6; } if test ${ac_cv_prog_CC+y} then : printf %s "(cached) " >&6 else $as_nop if test -n "$CC"; then ac_cv_prog_CC="$CC" # Let the user override the test. else as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS case $as_dir in #((( '') as_dir=./ ;; */) ;; *) as_dir=$as_dir/ ;; esac for ac_exec_ext in '' $ac_executable_extensions; do if as_fn_executable_p "$as_dir$ac_word$ac_exec_ext"; then ac_cv_prog_CC="${ac_tool_prefix}gcc" printf "%s\n" "$as_me:${as_lineno-$LINENO}: found $as_dir$ac_word$ac_exec_ext" >&5 break 2 fi done done IFS=$as_save_IFS fi fi CC=$ac_cv_prog_CC if test -n "$CC"; then { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $CC" >&5 printf "%s\n" "$CC" >&6; } else { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5 printf "%s\n" "no" >&6; } fi fi if test -z "$ac_cv_prog_CC"; then ac_ct_CC=$CC # Extract the first word of "gcc", so it can be a program name with args. set dummy gcc; ac_word=$2 { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 printf %s "checking for $ac_word... " >&6; } if test ${ac_cv_prog_ac_ct_CC+y} then : printf %s "(cached) " >&6 else $as_nop if test -n "$ac_ct_CC"; then ac_cv_prog_ac_ct_CC="$ac_ct_CC" # Let the user override the test. else as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS case $as_dir in #((( '') as_dir=./ ;; */) ;; *) as_dir=$as_dir/ ;; esac for ac_exec_ext in '' $ac_executable_extensions; do if as_fn_executable_p "$as_dir$ac_word$ac_exec_ext"; then ac_cv_prog_ac_ct_CC="gcc" printf "%s\n" "$as_me:${as_lineno-$LINENO}: found $as_dir$ac_word$ac_exec_ext" >&5 break 2 fi done done IFS=$as_save_IFS fi fi ac_ct_CC=$ac_cv_prog_ac_ct_CC if test -n "$ac_ct_CC"; then { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_ct_CC" >&5 printf "%s\n" "$ac_ct_CC" >&6; } else { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5 printf "%s\n" "no" >&6; } fi if test "x$ac_ct_CC" = x; then CC="" else case $cross_compiling:$ac_tool_warned in yes:) { printf "%s\n" "$as_me:${as_lineno-$LINENO}: WARNING: using cross tools not prefixed with host triplet" >&5 printf "%s\n" "$as_me: WARNING: using cross tools not prefixed with host triplet" >&2;} ac_tool_warned=yes ;; esac CC=$ac_ct_CC fi else CC="$ac_cv_prog_CC" fi if test -z "$CC"; then if test -n "$ac_tool_prefix"; then # Extract the first word of "${ac_tool_prefix}cc", so it can be a program name with args. set dummy ${ac_tool_prefix}cc; ac_word=$2 { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 printf %s "checking for $ac_word... " >&6; } if test ${ac_cv_prog_CC+y} then : printf %s "(cached) " >&6 else $as_nop if test -n "$CC"; then ac_cv_prog_CC="$CC" # Let the user override the test. else as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS case $as_dir in #((( '') as_dir=./ ;; */) ;; *) as_dir=$as_dir/ ;; esac for ac_exec_ext in '' $ac_executable_extensions; do if as_fn_executable_p "$as_dir$ac_word$ac_exec_ext"; then ac_cv_prog_CC="${ac_tool_prefix}cc" printf "%s\n" "$as_me:${as_lineno-$LINENO}: found $as_dir$ac_word$ac_exec_ext" >&5 break 2 fi done done IFS=$as_save_IFS fi fi CC=$ac_cv_prog_CC if test -n "$CC"; then { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $CC" >&5 printf "%s\n" "$CC" >&6; } else { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5 printf "%s\n" "no" >&6; } fi fi fi if test -z "$CC"; then # Extract the first word of "cc", so it can be a program name with args. set dummy cc; ac_word=$2 { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 printf %s "checking for $ac_word... " >&6; } if test ${ac_cv_prog_CC+y} then : printf %s "(cached) " >&6 else $as_nop if test -n "$CC"; then ac_cv_prog_CC="$CC" # Let the user override the test. else ac_prog_rejected=no as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS case $as_dir in #((( '') as_dir=./ ;; */) ;; *) as_dir=$as_dir/ ;; esac for ac_exec_ext in '' $ac_executable_extensions; do if as_fn_executable_p "$as_dir$ac_word$ac_exec_ext"; then if test "$as_dir$ac_word$ac_exec_ext" = "/usr/ucb/cc"; then ac_prog_rejected=yes continue fi ac_cv_prog_CC="cc" printf "%s\n" "$as_me:${as_lineno-$LINENO}: found $as_dir$ac_word$ac_exec_ext" >&5 break 2 fi done done IFS=$as_save_IFS if test $ac_prog_rejected = yes; then # We found a bogon in the path, so make sure we never use it. set dummy $ac_cv_prog_CC shift if test $# != 0; then # We chose a different compiler from the bogus one. # However, it has the same basename, so the bogon will be chosen # first if we set CC to just the basename; use the full file name. shift ac_cv_prog_CC="$as_dir$ac_word${1+' '}$@" fi fi fi fi CC=$ac_cv_prog_CC if test -n "$CC"; then { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $CC" >&5 printf "%s\n" "$CC" >&6; } else { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5 printf "%s\n" "no" >&6; } fi fi if test -z "$CC"; then if test -n "$ac_tool_prefix"; then for ac_prog in cl.exe do # Extract the first word of "$ac_tool_prefix$ac_prog", so it can be a program name with args. set dummy $ac_tool_prefix$ac_prog; ac_word=$2 { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 printf %s "checking for $ac_word... " >&6; } if test ${ac_cv_prog_CC+y} then : printf %s "(cached) " >&6 else $as_nop if test -n "$CC"; then ac_cv_prog_CC="$CC" # Let the user override the test. else as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS case $as_dir in #((( '') as_dir=./ ;; */) ;; *) as_dir=$as_dir/ ;; esac for ac_exec_ext in '' $ac_executable_extensions; do if as_fn_executable_p "$as_dir$ac_word$ac_exec_ext"; then ac_cv_prog_CC="$ac_tool_prefix$ac_prog" printf "%s\n" "$as_me:${as_lineno-$LINENO}: found $as_dir$ac_word$ac_exec_ext" >&5 break 2 fi done done IFS=$as_save_IFS fi fi CC=$ac_cv_prog_CC if test -n "$CC"; then { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $CC" >&5 printf "%s\n" "$CC" >&6; } else { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5 printf "%s\n" "no" >&6; } fi test -n "$CC" && break done fi if test -z "$CC"; then ac_ct_CC=$CC for ac_prog in cl.exe do # Extract the first word of "$ac_prog", so it can be a program name with args. set dummy $ac_prog; ac_word=$2 { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 printf %s "checking for $ac_word... " >&6; } if test ${ac_cv_prog_ac_ct_CC+y} then : printf %s "(cached) " >&6 else $as_nop if test -n "$ac_ct_CC"; then ac_cv_prog_ac_ct_CC="$ac_ct_CC" # Let the user override the test. else as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS case $as_dir in #((( '') as_dir=./ ;; */) ;; *) as_dir=$as_dir/ ;; esac for ac_exec_ext in '' $ac_executable_extensions; do if as_fn_executable_p "$as_dir$ac_word$ac_exec_ext"; then ac_cv_prog_ac_ct_CC="$ac_prog" printf "%s\n" "$as_me:${as_lineno-$LINENO}: found $as_dir$ac_word$ac_exec_ext" >&5 break 2 fi done done IFS=$as_save_IFS fi fi ac_ct_CC=$ac_cv_prog_ac_ct_CC if test -n "$ac_ct_CC"; then { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_ct_CC" >&5 printf "%s\n" "$ac_ct_CC" >&6; } else { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5 printf "%s\n" "no" >&6; } fi test -n "$ac_ct_CC" && break done if test "x$ac_ct_CC" = x; then CC="" else case $cross_compiling:$ac_tool_warned in yes:) { printf "%s\n" "$as_me:${as_lineno-$LINENO}: WARNING: using cross tools not prefixed with host triplet" >&5 printf "%s\n" "$as_me: WARNING: using cross tools not prefixed with host triplet" >&2;} ac_tool_warned=yes ;; esac CC=$ac_ct_CC fi fi fi if test -z "$CC"; then if test -n "$ac_tool_prefix"; then # Extract the first word of "${ac_tool_prefix}clang", so it can be a program name with args. set dummy ${ac_tool_prefix}clang; ac_word=$2 { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 printf %s "checking for $ac_word... " >&6; } if test ${ac_cv_prog_CC+y} then : printf %s "(cached) " >&6 else $as_nop if test -n "$CC"; then ac_cv_prog_CC="$CC" # Let the user override the test. else as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS case $as_dir in #((( '') as_dir=./ ;; */) ;; *) as_dir=$as_dir/ ;; esac for ac_exec_ext in '' $ac_executable_extensions; do if as_fn_executable_p "$as_dir$ac_word$ac_exec_ext"; then ac_cv_prog_CC="${ac_tool_prefix}clang" printf "%s\n" "$as_me:${as_lineno-$LINENO}: found $as_dir$ac_word$ac_exec_ext" >&5 break 2 fi done done IFS=$as_save_IFS fi fi CC=$ac_cv_prog_CC if test -n "$CC"; then { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $CC" >&5 printf "%s\n" "$CC" >&6; } else { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5 printf "%s\n" "no" >&6; } fi fi if test -z "$ac_cv_prog_CC"; then ac_ct_CC=$CC # Extract the first word of "clang", so it can be a program name with args. set dummy clang; ac_word=$2 { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 printf %s "checking for $ac_word... " >&6; } if test ${ac_cv_prog_ac_ct_CC+y} then : printf %s "(cached) " >&6 else $as_nop if test -n "$ac_ct_CC"; then ac_cv_prog_ac_ct_CC="$ac_ct_CC" # Let the user override the test. else as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS case $as_dir in #((( '') as_dir=./ ;; */) ;; *) as_dir=$as_dir/ ;; esac for ac_exec_ext in '' $ac_executable_extensions; do if as_fn_executable_p "$as_dir$ac_word$ac_exec_ext"; then ac_cv_prog_ac_ct_CC="clang" printf "%s\n" "$as_me:${as_lineno-$LINENO}: found $as_dir$ac_word$ac_exec_ext" >&5 break 2 fi done done IFS=$as_save_IFS fi fi ac_ct_CC=$ac_cv_prog_ac_ct_CC if test -n "$ac_ct_CC"; then { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_ct_CC" >&5 printf "%s\n" "$ac_ct_CC" >&6; } else { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5 printf "%s\n" "no" >&6; } fi if test "x$ac_ct_CC" = x; then CC="" else case $cross_compiling:$ac_tool_warned in yes:) { printf "%s\n" "$as_me:${as_lineno-$LINENO}: WARNING: using cross tools not prefixed with host triplet" >&5 printf "%s\n" "$as_me: WARNING: using cross tools not prefixed with host triplet" >&2;} ac_tool_warned=yes ;; esac CC=$ac_ct_CC fi else CC="$ac_cv_prog_CC" fi fi test -z "$CC" && { { printf "%s\n" "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5 printf "%s\n" "$as_me: error: in \`$ac_pwd':" >&2;} as_fn_error $? "no acceptable C compiler found in \$PATH See \`config.log' for more details" "$LINENO" 5; } # Provide some information about the compiler. printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for C compiler version" >&5 set X $ac_compile ac_compiler=$2 for ac_option in --version -v -V -qversion -version; do { { ac_try="$ac_compiler $ac_option >&5" case "(($ac_try" in *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; *) ac_try_echo=$ac_try;; esac eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\"" printf "%s\n" "$ac_try_echo"; } >&5 (eval "$ac_compiler $ac_option >&5") 2>conftest.err ac_status=$? if test -s conftest.err; then sed '10a\ ... rest of stderr output deleted ... 10q' conftest.err >conftest.er1 cat conftest.er1 >&5 fi rm -f conftest.er1 conftest.err printf "%s\n" "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 test $ac_status = 0; } done cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ int main (void) { ; return 0; } _ACEOF ac_clean_files_save=$ac_clean_files ac_clean_files="$ac_clean_files a.out a.out.dSYM a.exe b.out" # Try to create an executable without -o first, disregard a.out. # It will help us diagnose broken compilers, and finding out an intuition # of exeext. { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking whether the C compiler works" >&5 printf %s "checking whether the C compiler works... " >&6; } ac_link_default=`printf "%s\n" "$ac_link" | sed 's/ -o *conftest[^ ]*//'` # The possible output files: ac_files="a.out conftest.exe conftest a.exe a_out.exe b.out conftest.*" ac_rmfiles= for ac_file in $ac_files do case $ac_file in *.$ac_ext | *.xcoff | *.tds | *.d | *.pdb | *.xSYM | *.bb | *.bbg | *.map | *.inf | *.dSYM | *.o | *.obj ) ;; * ) ac_rmfiles="$ac_rmfiles $ac_file";; esac done rm -f $ac_rmfiles if { { ac_try="$ac_link_default" case "(($ac_try" in *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; *) ac_try_echo=$ac_try;; esac eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\"" printf "%s\n" "$ac_try_echo"; } >&5 (eval "$ac_link_default") 2>&5 ac_status=$? printf "%s\n" "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 test $ac_status = 0; } then : # Autoconf-2.13 could set the ac_cv_exeext variable to `no'. # So ignore a value of `no', otherwise this would lead to `EXEEXT = no' # in a Makefile. We should not override ac_cv_exeext if it was cached, # so that the user can short-circuit this test for compilers unknown to # Autoconf. for ac_file in $ac_files '' do test -f "$ac_file" || continue case $ac_file in *.$ac_ext | *.xcoff | *.tds | *.d | *.pdb | *.xSYM | *.bb | *.bbg | *.map | *.inf | *.dSYM | *.o | *.obj ) ;; [ab].out ) # We found the default executable, but exeext='' is most # certainly right. break;; *.* ) if test ${ac_cv_exeext+y} && test "$ac_cv_exeext" != no; then :; else ac_cv_exeext=`expr "$ac_file" : '[^.]*\(\..*\)'` fi # We set ac_cv_exeext here because the later test for it is not # safe: cross compilers may not add the suffix if given an `-o' # argument, so we may need to know it at that point already. # Even if this section looks crufty: it has the advantage of # actually working. break;; * ) break;; esac done test "$ac_cv_exeext" = no && ac_cv_exeext= else $as_nop ac_file='' fi if test -z "$ac_file" then : { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5 printf "%s\n" "no" >&6; } printf "%s\n" "$as_me: failed program was:" >&5 sed 's/^/| /' conftest.$ac_ext >&5 { { printf "%s\n" "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5 printf "%s\n" "$as_me: error: in \`$ac_pwd':" >&2;} as_fn_error 77 "C compiler cannot create executables See \`config.log' for more details" "$LINENO" 5; } else $as_nop { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: yes" >&5 printf "%s\n" "yes" >&6; } fi { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for C compiler default output file name" >&5 printf %s "checking for C compiler default output file name... " >&6; } { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_file" >&5 printf "%s\n" "$ac_file" >&6; } ac_exeext=$ac_cv_exeext rm -f -r a.out a.out.dSYM a.exe conftest$ac_cv_exeext b.out ac_clean_files=$ac_clean_files_save { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for suffix of executables" >&5 printf %s "checking for suffix of executables... " >&6; } if { { ac_try="$ac_link" case "(($ac_try" in *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; *) ac_try_echo=$ac_try;; esac eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\"" printf "%s\n" "$ac_try_echo"; } >&5 (eval "$ac_link") 2>&5 ac_status=$? printf "%s\n" "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 test $ac_status = 0; } then : # If both `conftest.exe' and `conftest' are `present' (well, observable) # catch `conftest.exe'. For instance with Cygwin, `ls conftest' will # work properly (i.e., refer to `conftest.exe'), while it won't with # `rm'. for ac_file in conftest.exe conftest conftest.*; do test -f "$ac_file" || continue case $ac_file in *.$ac_ext | *.xcoff | *.tds | *.d | *.pdb | *.xSYM | *.bb | *.bbg | *.map | *.inf | *.dSYM | *.o | *.obj ) ;; *.* ) ac_cv_exeext=`expr "$ac_file" : '[^.]*\(\..*\)'` break;; * ) break;; esac done else $as_nop { { printf "%s\n" "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5 printf "%s\n" "$as_me: error: in \`$ac_pwd':" >&2;} as_fn_error $? "cannot compute suffix of executables: cannot compile and link See \`config.log' for more details" "$LINENO" 5; } fi rm -f conftest conftest$ac_cv_exeext { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_exeext" >&5 printf "%s\n" "$ac_cv_exeext" >&6; } rm -f conftest.$ac_ext EXEEXT=$ac_cv_exeext ac_exeext=$EXEEXT cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ #include int main (void) { FILE *f = fopen ("conftest.out", "w"); return ferror (f) || fclose (f) != 0; ; return 0; } _ACEOF ac_clean_files="$ac_clean_files conftest.out" # Check that the compiler produces executables we can run. If not, either # the compiler is broken, or we cross compile. { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking whether we are cross compiling" >&5 printf %s "checking whether we are cross compiling... " >&6; } if test "$cross_compiling" != yes; then { { ac_try="$ac_link" case "(($ac_try" in *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; *) ac_try_echo=$ac_try;; esac eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\"" printf "%s\n" "$ac_try_echo"; } >&5 (eval "$ac_link") 2>&5 ac_status=$? printf "%s\n" "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 test $ac_status = 0; } if { ac_try='./conftest$ac_cv_exeext' { { case "(($ac_try" in *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; *) ac_try_echo=$ac_try;; esac eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\"" printf "%s\n" "$ac_try_echo"; } >&5 (eval "$ac_try") 2>&5 ac_status=$? printf "%s\n" "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 test $ac_status = 0; }; }; then cross_compiling=no else if test "$cross_compiling" = maybe; then cross_compiling=yes else { { printf "%s\n" "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5 printf "%s\n" "$as_me: error: in \`$ac_pwd':" >&2;} as_fn_error 77 "cannot run C compiled programs. If you meant to cross compile, use \`--host'. See \`config.log' for more details" "$LINENO" 5; } fi fi fi { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $cross_compiling" >&5 printf "%s\n" "$cross_compiling" >&6; } rm -f conftest.$ac_ext conftest$ac_cv_exeext conftest.out ac_clean_files=$ac_clean_files_save { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for suffix of object files" >&5 printf %s "checking for suffix of object files... " >&6; } if test ${ac_cv_objext+y} then : printf %s "(cached) " >&6 else $as_nop cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ int main (void) { ; return 0; } _ACEOF rm -f conftest.o conftest.obj if { { ac_try="$ac_compile" case "(($ac_try" in *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; *) ac_try_echo=$ac_try;; esac eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\"" printf "%s\n" "$ac_try_echo"; } >&5 (eval "$ac_compile") 2>&5 ac_status=$? printf "%s\n" "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 test $ac_status = 0; } then : for ac_file in conftest.o conftest.obj conftest.*; do test -f "$ac_file" || continue; case $ac_file in *.$ac_ext | *.xcoff | *.tds | *.d | *.pdb | *.xSYM | *.bb | *.bbg | *.map | *.inf | *.dSYM ) ;; *) ac_cv_objext=`expr "$ac_file" : '.*\.\(.*\)'` break;; esac done else $as_nop printf "%s\n" "$as_me: failed program was:" >&5 sed 's/^/| /' conftest.$ac_ext >&5 { { printf "%s\n" "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5 printf "%s\n" "$as_me: error: in \`$ac_pwd':" >&2;} as_fn_error $? "cannot compute suffix of object files: cannot compile See \`config.log' for more details" "$LINENO" 5; } fi rm -f conftest.$ac_cv_objext conftest.$ac_ext fi { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_objext" >&5 printf "%s\n" "$ac_cv_objext" >&6; } OBJEXT=$ac_cv_objext ac_objext=$OBJEXT { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking whether the compiler supports GNU C" >&5 printf %s "checking whether the compiler supports GNU C... " >&6; } if test ${ac_cv_c_compiler_gnu+y} then : printf %s "(cached) " >&6 else $as_nop cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ int main (void) { #ifndef __GNUC__ choke me #endif ; return 0; } _ACEOF if ac_fn_c_try_compile "$LINENO" then : ac_compiler_gnu=yes else $as_nop ac_compiler_gnu=no fi rm -f core conftest.err conftest.$ac_objext conftest.beam conftest.$ac_ext ac_cv_c_compiler_gnu=$ac_compiler_gnu fi { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_c_compiler_gnu" >&5 printf "%s\n" "$ac_cv_c_compiler_gnu" >&6; } ac_compiler_gnu=$ac_cv_c_compiler_gnu if test $ac_compiler_gnu = yes; then GCC=yes else GCC= fi ac_test_CFLAGS=${CFLAGS+y} ac_save_CFLAGS=$CFLAGS { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking whether $CC accepts -g" >&5 printf %s "checking whether $CC accepts -g... " >&6; } if test ${ac_cv_prog_cc_g+y} then : printf %s "(cached) " >&6 else $as_nop ac_save_c_werror_flag=$ac_c_werror_flag ac_c_werror_flag=yes ac_cv_prog_cc_g=no CFLAGS="-g" cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ int main (void) { ; return 0; } _ACEOF if ac_fn_c_try_compile "$LINENO" then : ac_cv_prog_cc_g=yes else $as_nop CFLAGS="" cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ int main (void) { ; return 0; } _ACEOF if ac_fn_c_try_compile "$LINENO" then : else $as_nop ac_c_werror_flag=$ac_save_c_werror_flag CFLAGS="-g" cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ int main (void) { ; return 0; } _ACEOF if ac_fn_c_try_compile "$LINENO" then : ac_cv_prog_cc_g=yes fi rm -f core conftest.err conftest.$ac_objext conftest.beam conftest.$ac_ext fi rm -f core conftest.err conftest.$ac_objext conftest.beam conftest.$ac_ext fi rm -f core conftest.err conftest.$ac_objext conftest.beam conftest.$ac_ext ac_c_werror_flag=$ac_save_c_werror_flag fi { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_prog_cc_g" >&5 printf "%s\n" "$ac_cv_prog_cc_g" >&6; } if test $ac_test_CFLAGS; then CFLAGS=$ac_save_CFLAGS elif test $ac_cv_prog_cc_g = yes; then if test "$GCC" = yes; then CFLAGS="-g -O2" else CFLAGS="-g" fi else if test "$GCC" = yes; then CFLAGS="-O2" else CFLAGS= fi fi ac_prog_cc_stdc=no if test x$ac_prog_cc_stdc = xno then : { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for $CC option to enable C11 features" >&5 printf %s "checking for $CC option to enable C11 features... " >&6; } if test ${ac_cv_prog_cc_c11+y} then : printf %s "(cached) " >&6 else $as_nop ac_cv_prog_cc_c11=no ac_save_CC=$CC cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ $ac_c_conftest_c11_program _ACEOF for ac_arg in '' -std=gnu11 do CC="$ac_save_CC $ac_arg" if ac_fn_c_try_compile "$LINENO" then : ac_cv_prog_cc_c11=$ac_arg fi rm -f core conftest.err conftest.$ac_objext conftest.beam test "x$ac_cv_prog_cc_c11" != "xno" && break done rm -f conftest.$ac_ext CC=$ac_save_CC fi if test "x$ac_cv_prog_cc_c11" = xno then : { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: unsupported" >&5 printf "%s\n" "unsupported" >&6; } else $as_nop if test "x$ac_cv_prog_cc_c11" = x then : { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: none needed" >&5 printf "%s\n" "none needed" >&6; } else $as_nop { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_prog_cc_c11" >&5 printf "%s\n" "$ac_cv_prog_cc_c11" >&6; } CC="$CC $ac_cv_prog_cc_c11" fi ac_cv_prog_cc_stdc=$ac_cv_prog_cc_c11 ac_prog_cc_stdc=c11 fi fi if test x$ac_prog_cc_stdc = xno then : { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for $CC option to enable C99 features" >&5 printf %s "checking for $CC option to enable C99 features... " >&6; } if test ${ac_cv_prog_cc_c99+y} then : printf %s "(cached) " >&6 else $as_nop ac_cv_prog_cc_c99=no ac_save_CC=$CC cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ $ac_c_conftest_c99_program _ACEOF for ac_arg in '' -std=gnu99 -std=c99 -c99 -qlanglvl=extc1x -qlanglvl=extc99 -AC99 -D_STDC_C99= do CC="$ac_save_CC $ac_arg" if ac_fn_c_try_compile "$LINENO" then : ac_cv_prog_cc_c99=$ac_arg fi rm -f core conftest.err conftest.$ac_objext conftest.beam test "x$ac_cv_prog_cc_c99" != "xno" && break done rm -f conftest.$ac_ext CC=$ac_save_CC fi if test "x$ac_cv_prog_cc_c99" = xno then : { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: unsupported" >&5 printf "%s\n" "unsupported" >&6; } else $as_nop if test "x$ac_cv_prog_cc_c99" = x then : { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: none needed" >&5 printf "%s\n" "none needed" >&6; } else $as_nop { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_prog_cc_c99" >&5 printf "%s\n" "$ac_cv_prog_cc_c99" >&6; } CC="$CC $ac_cv_prog_cc_c99" fi ac_cv_prog_cc_stdc=$ac_cv_prog_cc_c99 ac_prog_cc_stdc=c99 fi fi if test x$ac_prog_cc_stdc = xno then : { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for $CC option to enable C89 features" >&5 printf %s "checking for $CC option to enable C89 features... " >&6; } if test ${ac_cv_prog_cc_c89+y} then : printf %s "(cached) " >&6 else $as_nop ac_cv_prog_cc_c89=no ac_save_CC=$CC cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ $ac_c_conftest_c89_program _ACEOF for ac_arg in '' -qlanglvl=extc89 -qlanglvl=ansi -std -Ae "-Aa -D_HPUX_SOURCE" "-Xc -D__EXTENSIONS__" do CC="$ac_save_CC $ac_arg" if ac_fn_c_try_compile "$LINENO" then : ac_cv_prog_cc_c89=$ac_arg fi rm -f core conftest.err conftest.$ac_objext conftest.beam test "x$ac_cv_prog_cc_c89" != "xno" && break done rm -f conftest.$ac_ext CC=$ac_save_CC fi if test "x$ac_cv_prog_cc_c89" = xno then : { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: unsupported" >&5 printf "%s\n" "unsupported" >&6; } else $as_nop if test "x$ac_cv_prog_cc_c89" = x then : { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: none needed" >&5 printf "%s\n" "none needed" >&6; } else $as_nop { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_prog_cc_c89" >&5 printf "%s\n" "$ac_cv_prog_cc_c89" >&6; } CC="$CC $ac_cv_prog_cc_c89" fi ac_cv_prog_cc_stdc=$ac_cv_prog_cc_c89 ac_prog_cc_stdc=c89 fi fi ac_ext=c ac_cpp='$CPP $CPPFLAGS' ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5' ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' ac_compiler_gnu=$ac_cv_c_compiler_gnu ac_header= ac_cache= for ac_item in $ac_header_c_list do if test $ac_cache; then ac_fn_c_check_header_compile "$LINENO" $ac_header ac_cv_header_$ac_cache "$ac_includes_default" if eval test \"x\$ac_cv_header_$ac_cache\" = xyes; then printf "%s\n" "#define $ac_item 1" >> confdefs.h fi ac_header= ac_cache= elif test $ac_header; then ac_cache=$ac_item else ac_header=$ac_item fi done if test $ac_cv_header_stdlib_h = yes && test $ac_cv_header_string_h = yes then : printf "%s\n" "#define STDC_HEADERS 1" >>confdefs.h fi for ac_header in linux/landlock.h do : ac_fn_c_check_header_compile "$LINENO" "linux/landlock.h" "ac_cv_header_linux_landlock_h" "$ac_includes_default" if test "x$ac_cv_header_linux_landlock_h" = xyes then : printf "%s\n" "#define HAVE_LINUX_LANDLOCK_H 1" >>confdefs.h printf "%s\n" "#define HAVE_LANDLOCK 1" >>confdefs.h fi done cat >confcache <<\_ACEOF # This file is a shell script that caches the results of configure # tests run on this system so they can be shared between configure # scripts and configure runs, see configure's option --config-cache. # It is not useful on other systems. If it contains results you don't # want to keep, you may remove or edit it. # # config.status only pays attention to the cache file if you give it # the --recheck option to rerun configure. # # `ac_cv_env_foo' variables (set or unset) will be overridden when # loading this file, other *unset* `ac_cv_foo' will be assigned the # following values. _ACEOF # The following way of writing the cache mishandles newlines in values, # but we know of no workaround that is simple, portable, and efficient. # So, we kill variables containing newlines. # Ultrix sh set writes to stderr and can't be redirected directly, # and sets the high bit in the cache file unless we assign to the vars. ( for ac_var in `(set) 2>&1 | sed -n 's/^\([a-zA-Z_][a-zA-Z0-9_]*\)=.*/\1/p'`; do eval ac_val=\$$ac_var case $ac_val in #( *${as_nl}*) case $ac_var in #( *_cv_*) { printf "%s\n" "$as_me:${as_lineno-$LINENO}: WARNING: cache variable $ac_var contains a newline" >&5 printf "%s\n" "$as_me: WARNING: cache variable $ac_var contains a newline" >&2;} ;; esac case $ac_var in #( _ | IFS | as_nl) ;; #( BASH_ARGV | BASH_SOURCE) eval $ac_var= ;; #( *) { eval $ac_var=; unset $ac_var;} ;; esac ;; esac done (set) 2>&1 | case $as_nl`(ac_space=' '; set) 2>&1` in #( *${as_nl}ac_space=\ *) # `set' does not quote correctly, so add quotes: double-quote # substitution turns \\\\ into \\, and sed turns \\ into \. sed -n \ "s/'/'\\\\''/g; s/^\\([_$as_cr_alnum]*_cv_[_$as_cr_alnum]*\\)=\\(.*\\)/\\1='\\2'/p" ;; #( *) # `set' quotes correctly as required by POSIX, so do not add quotes. sed -n "/^[_$as_cr_alnum]*_cv_[_$as_cr_alnum]*=/p" ;; esac | sort ) | sed ' /^ac_cv_env_/b end t clear :clear s/^\([^=]*\)=\(.*[{}].*\)$/test ${\1+y} || &/ t end s/^\([^=]*\)=\(.*\)$/\1=${\1=\2}/ :end' >>confcache if diff "$cache_file" confcache >/dev/null 2>&1; then :; else if test -w "$cache_file"; then if test "x$cache_file" != "x/dev/null"; then { printf "%s\n" "$as_me:${as_lineno-$LINENO}: updating cache $cache_file" >&5 printf "%s\n" "$as_me: updating cache $cache_file" >&6;} if test ! -f "$cache_file" || test -h "$cache_file"; then cat confcache >"$cache_file" else case $cache_file in #( */* | ?:*) mv -f confcache "$cache_file"$$ && mv -f "$cache_file"$$ "$cache_file" ;; #( *) mv -f confcache "$cache_file" ;; esac fi fi else { printf "%s\n" "$as_me:${as_lineno-$LINENO}: not updating unwritable cache $cache_file" >&5 printf "%s\n" "$as_me: not updating unwritable cache $cache_file" >&6;} fi fi rm -f confcache test "x$prefix" = xNONE && prefix=$ac_default_prefix # Let make expand exec_prefix. test "x$exec_prefix" = xNONE && exec_prefix='${prefix}' DEFS=-DHAVE_CONFIG_H ac_libobjs= ac_ltlibobjs= U= for ac_i in : $LIBOBJS; do test "x$ac_i" = x: && continue # 1. Remove the extension, and $U if already installed. ac_script='s/\$U\././;s/\.o$//;s/\.obj$//' ac_i=`printf "%s\n" "$ac_i" | sed "$ac_script"` # 2. Prepend LIBOBJDIR. When used with automake>=1.10 LIBOBJDIR # will be set to the directory where LIBOBJS objects are built. as_fn_append ac_libobjs " \${LIBOBJDIR}$ac_i\$U.$ac_objext" as_fn_append ac_ltlibobjs " \${LIBOBJDIR}$ac_i"'$U.lo' done LIBOBJS=$ac_libobjs LTLIBOBJS=$ac_ltlibobjs : "${CONFIG_STATUS=./config.status}" ac_write_fail=0 ac_clean_files_save=$ac_clean_files ac_clean_files="$ac_clean_files $CONFIG_STATUS" { printf "%s\n" "$as_me:${as_lineno-$LINENO}: creating $CONFIG_STATUS" >&5 printf "%s\n" "$as_me: creating $CONFIG_STATUS" >&6;} as_write_fail=0 cat >$CONFIG_STATUS <<_ASEOF || as_write_fail=1 #! $SHELL # Generated by $as_me. # Run this file to recreate the current configuration. # Compiler output produced by configure, useful for debugging # configure, is in config.log if it exists. debug=false ac_cs_recheck=false ac_cs_silent=false SHELL=\${CONFIG_SHELL-$SHELL} export SHELL _ASEOF cat >>$CONFIG_STATUS <<\_ASEOF || as_write_fail=1 ## -------------------- ## ## M4sh Initialization. ## ## -------------------- ## # Be more Bourne compatible DUALCASE=1; export DUALCASE # for MKS sh as_nop=: if test ${ZSH_VERSION+y} && (emulate sh) >/dev/null 2>&1 then : emulate sh NULLCMD=: # Pre-4.2 versions of Zsh do word splitting on ${1+"$@"}, which # is contrary to our usage. Disable this feature. alias -g '${1+"$@"}'='"$@"' setopt NO_GLOB_SUBST else $as_nop case `(set -o) 2>/dev/null` in #( *posix*) : set -o posix ;; #( *) : ;; esac fi # Reset variables that may have inherited troublesome values from # the environment. # IFS needs to be set, to space, tab, and newline, in precisely that order. # (If _AS_PATH_WALK were called with IFS unset, it would have the # side effect of setting IFS to empty, thus disabling word splitting.) # Quoting is to prevent editors from complaining about space-tab. as_nl=' ' export as_nl IFS=" "" $as_nl" PS1='$ ' PS2='> ' PS4='+ ' # Ensure predictable behavior from utilities with locale-dependent output. LC_ALL=C export LC_ALL LANGUAGE=C export LANGUAGE # We cannot yet rely on "unset" to work, but we need these variables # to be unset--not just set to an empty or harmless value--now, to # avoid bugs in old shells (e.g. pre-3.0 UWIN ksh). This construct # also avoids known problems related to "unset" and subshell syntax # in other old shells (e.g. bash 2.01 and pdksh 5.2.14). for as_var in BASH_ENV ENV MAIL MAILPATH CDPATH do eval test \${$as_var+y} \ && ( (unset $as_var) || exit 1) >/dev/null 2>&1 && unset $as_var || : done # Ensure that fds 0, 1, and 2 are open. if (exec 3>&0) 2>/dev/null; then :; else exec 0&1) 2>/dev/null; then :; else exec 1>/dev/null; fi if (exec 3>&2) ; then :; else exec 2>/dev/null; fi # The user is always right. if ${PATH_SEPARATOR+false} :; then PATH_SEPARATOR=: (PATH='/bin;/bin'; FPATH=$PATH; sh -c :) >/dev/null 2>&1 && { (PATH='/bin:/bin'; FPATH=$PATH; sh -c :) >/dev/null 2>&1 || PATH_SEPARATOR=';' } fi # Find who we are. Look in the path if we contain no directory separator. as_myself= case $0 in #(( *[\\/]* ) as_myself=$0 ;; *) as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS case $as_dir in #((( '') as_dir=./ ;; */) ;; *) as_dir=$as_dir/ ;; esac test -r "$as_dir$0" && as_myself=$as_dir$0 && break done IFS=$as_save_IFS ;; esac # We did not find ourselves, most probably we were run as `sh COMMAND' # in which case we are not to be found in the path. if test "x$as_myself" = x; then as_myself=$0 fi if test ! -f "$as_myself"; then printf "%s\n" "$as_myself: error: cannot find myself; rerun with an absolute file name" >&2 exit 1 fi # as_fn_error STATUS ERROR [LINENO LOG_FD] # ---------------------------------------- # Output "`basename $0`: error: ERROR" to stderr. If LINENO and LOG_FD are # provided, also output the error to LOG_FD, referencing LINENO. Then exit the # script with STATUS, using 1 if that was 0. as_fn_error () { as_status=$1; test $as_status -eq 0 && as_status=1 if test "$4"; then as_lineno=${as_lineno-"$3"} as_lineno_stack=as_lineno_stack=$as_lineno_stack printf "%s\n" "$as_me:${as_lineno-$LINENO}: error: $2" >&$4 fi printf "%s\n" "$as_me: error: $2" >&2 as_fn_exit $as_status } # as_fn_error # as_fn_set_status STATUS # ----------------------- # Set $? to STATUS, without forking. as_fn_set_status () { return $1 } # as_fn_set_status # as_fn_exit STATUS # ----------------- # Exit the shell with STATUS, even in a "trap 0" or "set -e" context. as_fn_exit () { set +e as_fn_set_status $1 exit $1 } # as_fn_exit # as_fn_unset VAR # --------------- # Portably unset VAR. as_fn_unset () { { eval $1=; unset $1;} } as_unset=as_fn_unset # as_fn_append VAR VALUE # ---------------------- # Append the text in VALUE to the end of the definition contained in VAR. Take # advantage of any shell optimizations that allow amortized linear growth over # repeated appends, instead of the typical quadratic growth present in naive # implementations. if (eval "as_var=1; as_var+=2; test x\$as_var = x12") 2>/dev/null then : eval 'as_fn_append () { eval $1+=\$2 }' else $as_nop as_fn_append () { eval $1=\$$1\$2 } fi # as_fn_append # as_fn_arith ARG... # ------------------ # Perform arithmetic evaluation on the ARGs, and store the result in the # global $as_val. Take advantage of shells that can avoid forks. The arguments # must be portable across $(()) and expr. if (eval "test \$(( 1 + 1 )) = 2") 2>/dev/null then : eval 'as_fn_arith () { as_val=$(( $* )) }' else $as_nop as_fn_arith () { as_val=`expr "$@" || test $? -eq 1` } fi # as_fn_arith if expr a : '\(a\)' >/dev/null 2>&1 && test "X`expr 00001 : '.*\(...\)'`" = X001; then as_expr=expr else as_expr=false fi if (basename -- /) >/dev/null 2>&1 && test "X`basename -- / 2>&1`" = "X/"; then as_basename=basename else as_basename=false fi if (as_dir=`dirname -- /` && test "X$as_dir" = X/) >/dev/null 2>&1; then as_dirname=dirname else as_dirname=false fi as_me=`$as_basename -- "$0" || $as_expr X/"$0" : '.*/\([^/][^/]*\)/*$' \| \ X"$0" : 'X\(//\)$' \| \ X"$0" : 'X\(/\)' \| . 2>/dev/null || printf "%s\n" X/"$0" | sed '/^.*\/\([^/][^/]*\)\/*$/{ s//\1/ q } /^X\/\(\/\/\)$/{ s//\1/ q } /^X\/\(\/\).*/{ s//\1/ q } s/.*/./; q'` # Avoid depending upon Character Ranges. as_cr_letters='abcdefghijklmnopqrstuvwxyz' as_cr_LETTERS='ABCDEFGHIJKLMNOPQRSTUVWXYZ' as_cr_Letters=$as_cr_letters$as_cr_LETTERS as_cr_digits='0123456789' as_cr_alnum=$as_cr_Letters$as_cr_digits # Determine whether it's possible to make 'echo' print without a newline. # These variables are no longer used directly by Autoconf, but are AC_SUBSTed # for compatibility with existing Makefiles. ECHO_C= ECHO_N= ECHO_T= case `echo -n x` in #((((( -n*) case `echo 'xy\c'` in *c*) ECHO_T=' ';; # ECHO_T is single tab character. xy) ECHO_C='\c';; *) echo `echo ksh88 bug on AIX 6.1` > /dev/null ECHO_T=' ';; esac;; *) ECHO_N='-n';; esac # For backward compatibility with old third-party macros, we provide # the shell variables $as_echo and $as_echo_n. New code should use # AS_ECHO(["message"]) and AS_ECHO_N(["message"]), respectively. as_echo='printf %s\n' as_echo_n='printf %s' rm -f conf$$ conf$$.exe conf$$.file if test -d conf$$.dir; then rm -f conf$$.dir/conf$$.file else rm -f conf$$.dir mkdir conf$$.dir 2>/dev/null fi if (echo >conf$$.file) 2>/dev/null; then if ln -s conf$$.file conf$$ 2>/dev/null; then as_ln_s='ln -s' # ... but there are two gotchas: # 1) On MSYS, both `ln -s file dir' and `ln file dir' fail. # 2) DJGPP < 2.04 has no symlinks; `ln -s' creates a wrapper executable. # In both cases, we have to default to `cp -pR'. ln -s conf$$.file conf$$.dir 2>/dev/null && test ! -f conf$$.exe || as_ln_s='cp -pR' elif ln conf$$.file conf$$ 2>/dev/null; then as_ln_s=ln else as_ln_s='cp -pR' fi else as_ln_s='cp -pR' fi rm -f conf$$ conf$$.exe conf$$.dir/conf$$.file conf$$.file rmdir conf$$.dir 2>/dev/null # as_fn_mkdir_p # ------------- # Create "$as_dir" as a directory, including parents if necessary. as_fn_mkdir_p () { case $as_dir in #( -*) as_dir=./$as_dir;; esac test -d "$as_dir" || eval $as_mkdir_p || { as_dirs= while :; do case $as_dir in #( *\'*) as_qdir=`printf "%s\n" "$as_dir" | sed "s/'/'\\\\\\\\''/g"`;; #'( *) as_qdir=$as_dir;; esac as_dirs="'$as_qdir' $as_dirs" as_dir=`$as_dirname -- "$as_dir" || $as_expr X"$as_dir" : 'X\(.*[^/]\)//*[^/][^/]*/*$' \| \ X"$as_dir" : 'X\(//\)[^/]' \| \ X"$as_dir" : 'X\(//\)$' \| \ X"$as_dir" : 'X\(/\)' \| . 2>/dev/null || printf "%s\n" X"$as_dir" | sed '/^X\(.*[^/]\)\/\/*[^/][^/]*\/*$/{ s//\1/ q } /^X\(\/\/\)[^/].*/{ s//\1/ q } /^X\(\/\/\)$/{ s//\1/ q } /^X\(\/\).*/{ s//\1/ q } s/.*/./; q'` test -d "$as_dir" && break done test -z "$as_dirs" || eval "mkdir $as_dirs" } || test -d "$as_dir" || as_fn_error $? "cannot create directory $as_dir" } # as_fn_mkdir_p if mkdir -p . 2>/dev/null; then as_mkdir_p='mkdir -p "$as_dir"' else test -d ./-p && rmdir ./-p as_mkdir_p=false fi # as_fn_executable_p FILE # ----------------------- # Test if FILE is an executable regular file. as_fn_executable_p () { test -f "$1" && test -x "$1" } # as_fn_executable_p as_test_x='test -x' as_executable_p=as_fn_executable_p # Sed expression to map a string onto a valid CPP name. as_tr_cpp="eval sed 'y%*$as_cr_letters%P$as_cr_LETTERS%;s%[^_$as_cr_alnum]%_%g'" # Sed expression to map a string onto a valid variable name. as_tr_sh="eval sed 'y%*+%pp%;s%[^_$as_cr_alnum]%_%g'" exec 6>&1 ## ----------------------------------- ## ## Main body of $CONFIG_STATUS script. ## ## ----------------------------------- ## _ASEOF test $as_write_fail = 0 && chmod +x $CONFIG_STATUS || ac_write_fail=1 cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1 # Save the log message, to keep $0 and so on meaningful, and to # report actual input values of CONFIG_FILES etc. instead of their # values after options handling. ac_log=" This file was extended by $as_me, which was generated by GNU Autoconf 2.71. Invocation command line was CONFIG_FILES = $CONFIG_FILES CONFIG_HEADERS = $CONFIG_HEADERS CONFIG_LINKS = $CONFIG_LINKS CONFIG_COMMANDS = $CONFIG_COMMANDS $ $0 $@ on `(hostname || uname -n) 2>/dev/null | sed 1q` " _ACEOF case $ac_config_files in *" "*) set x $ac_config_files; shift; ac_config_files=$*;; esac case $ac_config_headers in *" "*) set x $ac_config_headers; shift; ac_config_headers=$*;; esac cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1 # Files that config.status was made for. config_files="$ac_config_files" config_headers="$ac_config_headers" _ACEOF cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1 ac_cs_usage="\ \`$as_me' instantiates files and other configuration actions from templates according to the current configuration. Unless the files and actions are specified as TAGs, all are instantiated by default. Usage: $0 [OPTION]... [TAG]... -h, --help print this help, then exit -V, --version print version number and configuration settings, then exit --config print configuration, then exit -q, --quiet, --silent do not print progress messages -d, --debug don't remove temporary files --recheck update $as_me by reconfiguring in the same conditions --file=FILE[:TEMPLATE] instantiate the configuration file FILE --header=FILE[:TEMPLATE] instantiate the configuration header FILE Configuration files: $config_files Configuration headers: $config_headers Report bugs to the package provider." _ACEOF ac_cs_config=`printf "%s\n" "$ac_configure_args" | sed "$ac_safe_unquote"` ac_cs_config_escaped=`printf "%s\n" "$ac_cs_config" | sed "s/^ //; s/'/'\\\\\\\\''/g"` cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1 ac_cs_config='$ac_cs_config_escaped' ac_cs_version="\\ config.status configured by $0, generated by GNU Autoconf 2.71, with options \\"\$ac_cs_config\\" Copyright (C) 2021 Free Software Foundation, Inc. This config.status script is free software; the Free Software Foundation gives unlimited permission to copy, distribute and modify it." ac_pwd='$ac_pwd' srcdir='$srcdir' test -n "\$AWK" || AWK=awk _ACEOF cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1 # The default lists apply if the user does not specify any file. ac_need_defaults=: while test $# != 0 do case $1 in --*=?*) ac_option=`expr "X$1" : 'X\([^=]*\)='` ac_optarg=`expr "X$1" : 'X[^=]*=\(.*\)'` ac_shift=: ;; --*=) ac_option=`expr "X$1" : 'X\([^=]*\)='` ac_optarg= ac_shift=: ;; *) ac_option=$1 ac_optarg=$2 ac_shift=shift ;; esac case $ac_option in # Handling of the options. -recheck | --recheck | --rechec | --reche | --rech | --rec | --re | --r) ac_cs_recheck=: ;; --version | --versio | --versi | --vers | --ver | --ve | --v | -V ) printf "%s\n" "$ac_cs_version"; exit ;; --config | --confi | --conf | --con | --co | --c ) printf "%s\n" "$ac_cs_config"; exit ;; --debug | --debu | --deb | --de | --d | -d ) debug=: ;; --file | --fil | --fi | --f ) $ac_shift case $ac_optarg in *\'*) ac_optarg=`printf "%s\n" "$ac_optarg" | sed "s/'/'\\\\\\\\''/g"` ;; '') as_fn_error $? "missing file argument" ;; esac as_fn_append CONFIG_FILES " '$ac_optarg'" ac_need_defaults=false;; --header | --heade | --head | --hea ) $ac_shift case $ac_optarg in *\'*) ac_optarg=`printf "%s\n" "$ac_optarg" | sed "s/'/'\\\\\\\\''/g"` ;; esac as_fn_append CONFIG_HEADERS " '$ac_optarg'" ac_need_defaults=false;; --he | --h) # Conflict between --help and --header as_fn_error $? "ambiguous option: \`$1' Try \`$0 --help' for more information.";; --help | --hel | -h ) printf "%s\n" "$ac_cs_usage"; exit ;; -q | -quiet | --quiet | --quie | --qui | --qu | --q \ | -silent | --silent | --silen | --sile | --sil | --si | --s) ac_cs_silent=: ;; # This is an error. -*) as_fn_error $? "unrecognized option: \`$1' Try \`$0 --help' for more information." ;; *) as_fn_append ac_config_targets " $1" ac_need_defaults=false ;; esac shift done ac_configure_extra_args= if $ac_cs_silent; then exec 6>/dev/null ac_configure_extra_args="$ac_configure_extra_args --silent" fi _ACEOF cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1 if \$ac_cs_recheck; then set X $SHELL '$0' $ac_configure_args \$ac_configure_extra_args --no-create --no-recursion shift \printf "%s\n" "running CONFIG_SHELL=$SHELL \$*" >&6 CONFIG_SHELL='$SHELL' export CONFIG_SHELL exec "\$@" fi _ACEOF cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1 exec 5>>config.log { echo sed 'h;s/./-/g;s/^.../## /;s/...$/ ##/;p;x;p;x' <<_ASBOX ## Running $as_me. ## _ASBOX printf "%s\n" "$ac_log" } >&5 _ACEOF cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1 _ACEOF cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1 # Handling of arguments. for ac_config_target in $ac_config_targets do case $ac_config_target in "config.h") CONFIG_HEADERS="$CONFIG_HEADERS config.h" ;; "Makefile") CONFIG_FILES="$CONFIG_FILES Makefile" ;; *) as_fn_error $? "invalid argument: \`$ac_config_target'" "$LINENO" 5;; esac done # If the user did not use the arguments to specify the items to instantiate, # then the envvar interface is used. Set only those that are not. # We use the long form for the default assignment because of an extremely # bizarre bug on SunOS 4.1.3. if $ac_need_defaults; then test ${CONFIG_FILES+y} || CONFIG_FILES=$config_files test ${CONFIG_HEADERS+y} || CONFIG_HEADERS=$config_headers fi # Have a temporary directory for convenience. Make it in the build tree # simply because there is no reason against having it here, and in addition, # creating and moving files from /tmp can sometimes cause problems. # Hook for its removal unless debugging. # Note that there is a small window in which the directory will not be cleaned: # after its creation but before its name has been assigned to `$tmp'. $debug || { tmp= ac_tmp= trap 'exit_status=$? : "${ac_tmp:=$tmp}" { test ! -d "$ac_tmp" || rm -fr "$ac_tmp"; } && exit $exit_status ' 0 trap 'as_fn_exit 1' 1 2 13 15 } # Create a (secure) tmp directory for tmp files. { tmp=`(umask 077 && mktemp -d "./confXXXXXX") 2>/dev/null` && test -d "$tmp" } || { tmp=./conf$$-$RANDOM (umask 077 && mkdir "$tmp") } || as_fn_error $? "cannot create a temporary directory in ." "$LINENO" 5 ac_tmp=$tmp # Set up the scripts for CONFIG_FILES section. # No need to generate them if there are no CONFIG_FILES. # This happens for instance with `./config.status config.h'. if test -n "$CONFIG_FILES"; then ac_cr=`echo X | tr X '\015'` # On cygwin, bash can eat \r inside `` if the user requested igncr. # But we know of no other shell where ac_cr would be empty at this # point, so we can use a bashism as a fallback. if test "x$ac_cr" = x; then eval ac_cr=\$\'\\r\' fi ac_cs_awk_cr=`$AWK 'BEGIN { print "a\rb" }' /dev/null` if test "$ac_cs_awk_cr" = "a${ac_cr}b"; then ac_cs_awk_cr='\\r' else ac_cs_awk_cr=$ac_cr fi echo 'BEGIN {' >"$ac_tmp/subs1.awk" && _ACEOF { echo "cat >conf$$subs.awk <<_ACEOF" && echo "$ac_subst_vars" | sed 's/.*/&!$&$ac_delim/' && echo "_ACEOF" } >conf$$subs.sh || as_fn_error $? "could not make $CONFIG_STATUS" "$LINENO" 5 ac_delim_num=`echo "$ac_subst_vars" | grep -c '^'` ac_delim='%!_!# ' for ac_last_try in false false false false false :; do . ./conf$$subs.sh || as_fn_error $? "could not make $CONFIG_STATUS" "$LINENO" 5 ac_delim_n=`sed -n "s/.*$ac_delim\$/X/p" conf$$subs.awk | grep -c X` if test $ac_delim_n = $ac_delim_num; then break elif $ac_last_try; then as_fn_error $? "could not make $CONFIG_STATUS" "$LINENO" 5 else ac_delim="$ac_delim!$ac_delim _$ac_delim!! " fi done rm -f conf$$subs.sh cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1 cat >>"\$ac_tmp/subs1.awk" <<\\_ACAWK && _ACEOF sed -n ' h s/^/S["/; s/!.*/"]=/ p g s/^[^!]*!// :repl t repl s/'"$ac_delim"'$// t delim :nl h s/\(.\{148\}\)..*/\1/ t more1 s/["\\]/\\&/g; s/^/"/; s/$/\\n"\\/ p n b repl :more1 s/["\\]/\\&/g; s/^/"/; s/$/"\\/ p g s/.\{148\}// t nl :delim h s/\(.\{148\}\)..*/\1/ t more2 s/["\\]/\\&/g; s/^/"/; s/$/"/ p b :more2 s/["\\]/\\&/g; s/^/"/; s/$/"\\/ p g s/.\{148\}// t delim ' >$CONFIG_STATUS || ac_write_fail=1 rm -f conf$$subs.awk cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1 _ACAWK cat >>"\$ac_tmp/subs1.awk" <<_ACAWK && for (key in S) S_is_set[key] = 1 FS = "" } { line = $ 0 nfields = split(line, field, "@") substed = 0 len = length(field[1]) for (i = 2; i < nfields; i++) { key = field[i] keylen = length(key) if (S_is_set[key]) { value = S[key] line = substr(line, 1, len) "" value "" substr(line, len + keylen + 3) len += length(value) + length(field[++i]) substed = 1 } else len += 1 + keylen } print line } _ACAWK _ACEOF cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1 if sed "s/$ac_cr//" < /dev/null > /dev/null 2>&1; then sed "s/$ac_cr\$//; s/$ac_cr/$ac_cs_awk_cr/g" else cat fi < "$ac_tmp/subs1.awk" > "$ac_tmp/subs.awk" \ || as_fn_error $? "could not setup config files machinery" "$LINENO" 5 _ACEOF # VPATH may cause trouble with some makes, so we remove sole $(srcdir), # ${srcdir} and @srcdir@ entries from VPATH if srcdir is ".", strip leading and # trailing colons and then remove the whole line if VPATH becomes empty # (actually we leave an empty line to preserve line numbers). if test "x$srcdir" = x.; then ac_vpsub='/^[ ]*VPATH[ ]*=[ ]*/{ h s/// s/^/:/ s/[ ]*$/:/ s/:\$(srcdir):/:/g s/:\${srcdir}:/:/g s/:@srcdir@:/:/g s/^:*// s/:*$// x s/\(=[ ]*\).*/\1/ G s/\n// s/^[^=]*=[ ]*$// }' fi cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1 fi # test -n "$CONFIG_FILES" # Set up the scripts for CONFIG_HEADERS section. # No need to generate them if there are no CONFIG_HEADERS. # This happens for instance with `./config.status Makefile'. if test -n "$CONFIG_HEADERS"; then cat >"$ac_tmp/defines.awk" <<\_ACAWK || BEGIN { _ACEOF # Transform confdefs.h into an awk script `defines.awk', embedded as # here-document in config.status, that substitutes the proper values into # config.h.in to produce config.h. # Create a delimiter string that does not exist in confdefs.h, to ease # handling of long lines. ac_delim='%!_!# ' for ac_last_try in false false :; do ac_tt=`sed -n "/$ac_delim/p" confdefs.h` if test -z "$ac_tt"; then break elif $ac_last_try; then as_fn_error $? "could not make $CONFIG_HEADERS" "$LINENO" 5 else ac_delim="$ac_delim!$ac_delim _$ac_delim!! " fi done # For the awk script, D is an array of macro values keyed by name, # likewise P contains macro parameters if any. Preserve backslash # newline sequences. ac_word_re=[_$as_cr_Letters][_$as_cr_alnum]* sed -n ' s/.\{148\}/&'"$ac_delim"'/g t rset :rset s/^[ ]*#[ ]*define[ ][ ]*/ / t def d :def s/\\$// t bsnl s/["\\]/\\&/g s/^ \('"$ac_word_re"'\)\(([^()]*)\)[ ]*\(.*\)/P["\1"]="\2"\ D["\1"]=" \3"/p s/^ \('"$ac_word_re"'\)[ ]*\(.*\)/D["\1"]=" \2"/p d :bsnl s/["\\]/\\&/g s/^ \('"$ac_word_re"'\)\(([^()]*)\)[ ]*\(.*\)/P["\1"]="\2"\ D["\1"]=" \3\\\\\\n"\\/p t cont s/^ \('"$ac_word_re"'\)[ ]*\(.*\)/D["\1"]=" \2\\\\\\n"\\/p t cont d :cont n s/.\{148\}/&'"$ac_delim"'/g t clear :clear s/\\$// t bsnlc s/["\\]/\\&/g; s/^/"/; s/$/"/p d :bsnlc s/["\\]/\\&/g; s/^/"/; s/$/\\\\\\n"\\/p b cont ' >$CONFIG_STATUS || ac_write_fail=1 cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1 for (key in D) D_is_set[key] = 1 FS = "" } /^[\t ]*#[\t ]*(define|undef)[\t ]+$ac_word_re([\t (]|\$)/ { line = \$ 0 split(line, arg, " ") if (arg[1] == "#") { defundef = arg[2] mac1 = arg[3] } else { defundef = substr(arg[1], 2) mac1 = arg[2] } split(mac1, mac2, "(") #) macro = mac2[1] prefix = substr(line, 1, index(line, defundef) - 1) if (D_is_set[macro]) { # Preserve the white space surrounding the "#". print prefix "define", macro P[macro] D[macro] next } else { # Replace #undef with comments. This is necessary, for example, # in the case of _POSIX_SOURCE, which is predefined and required # on some systems where configure will not decide to define it. if (defundef == "undef") { print "/*", prefix defundef, macro, "*/" next } } } { print } _ACAWK _ACEOF cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1 as_fn_error $? "could not setup config headers machinery" "$LINENO" 5 fi # test -n "$CONFIG_HEADERS" eval set X " :F $CONFIG_FILES :H $CONFIG_HEADERS " shift for ac_tag do case $ac_tag in :[FHLC]) ac_mode=$ac_tag; continue;; esac case $ac_mode$ac_tag in :[FHL]*:*);; :L* | :C*:*) as_fn_error $? "invalid tag \`$ac_tag'" "$LINENO" 5;; :[FH]-) ac_tag=-:-;; :[FH]*) ac_tag=$ac_tag:$ac_tag.in;; esac ac_save_IFS=$IFS IFS=: set x $ac_tag IFS=$ac_save_IFS shift ac_file=$1 shift case $ac_mode in :L) ac_source=$1;; :[FH]) ac_file_inputs= for ac_f do case $ac_f in -) ac_f="$ac_tmp/stdin";; *) # Look for the file first in the build tree, then in the source tree # (if the path is not absolute). The absolute path cannot be DOS-style, # because $ac_f cannot contain `:'. test -f "$ac_f" || case $ac_f in [\\/$]*) false;; *) test -f "$srcdir/$ac_f" && ac_f="$srcdir/$ac_f";; esac || as_fn_error 1 "cannot find input file: \`$ac_f'" "$LINENO" 5;; esac case $ac_f in *\'*) ac_f=`printf "%s\n" "$ac_f" | sed "s/'/'\\\\\\\\''/g"`;; esac as_fn_append ac_file_inputs " '$ac_f'" done # Let's still pretend it is `configure' which instantiates (i.e., don't # use $as_me), people would be surprised to read: # /* config.h. Generated by config.status. */ configure_input='Generated from '` printf "%s\n" "$*" | sed 's|^[^:]*/||;s|:[^:]*/|, |g' `' by configure.' if test x"$ac_file" != x-; then configure_input="$ac_file. $configure_input" { printf "%s\n" "$as_me:${as_lineno-$LINENO}: creating $ac_file" >&5 printf "%s\n" "$as_me: creating $ac_file" >&6;} fi # Neutralize special characters interpreted by sed in replacement strings. case $configure_input in #( *\&* | *\|* | *\\* ) ac_sed_conf_input=`printf "%s\n" "$configure_input" | sed 's/[\\\\&|]/\\\\&/g'`;; #( *) ac_sed_conf_input=$configure_input;; esac case $ac_tag in *:-:* | *:-) cat >"$ac_tmp/stdin" \ || as_fn_error $? "could not create $ac_file" "$LINENO" 5 ;; esac ;; esac ac_dir=`$as_dirname -- "$ac_file" || $as_expr X"$ac_file" : 'X\(.*[^/]\)//*[^/][^/]*/*$' \| \ X"$ac_file" : 'X\(//\)[^/]' \| \ X"$ac_file" : 'X\(//\)$' \| \ X"$ac_file" : 'X\(/\)' \| . 2>/dev/null || printf "%s\n" X"$ac_file" | sed '/^X\(.*[^/]\)\/\/*[^/][^/]*\/*$/{ s//\1/ q } /^X\(\/\/\)[^/].*/{ s//\1/ q } /^X\(\/\/\)$/{ s//\1/ q } /^X\(\/\).*/{ s//\1/ q } s/.*/./; q'` as_dir="$ac_dir"; as_fn_mkdir_p ac_builddir=. case "$ac_dir" in .) ac_dir_suffix= ac_top_builddir_sub=. ac_top_build_prefix= ;; *) ac_dir_suffix=/`printf "%s\n" "$ac_dir" | sed 's|^\.[\\/]||'` # A ".." for each directory in $ac_dir_suffix. ac_top_builddir_sub=`printf "%s\n" "$ac_dir_suffix" | sed 's|/[^\\/]*|/..|g;s|/||'` case $ac_top_builddir_sub in "") ac_top_builddir_sub=. ac_top_build_prefix= ;; *) ac_top_build_prefix=$ac_top_builddir_sub/ ;; esac ;; esac ac_abs_top_builddir=$ac_pwd ac_abs_builddir=$ac_pwd$ac_dir_suffix # for backward compatibility: ac_top_builddir=$ac_top_build_prefix case $srcdir in .) # We are building in place. ac_srcdir=. ac_top_srcdir=$ac_top_builddir_sub ac_abs_top_srcdir=$ac_pwd ;; [\\/]* | ?:[\\/]* ) # Absolute name. ac_srcdir=$srcdir$ac_dir_suffix; ac_top_srcdir=$srcdir ac_abs_top_srcdir=$srcdir ;; *) # Relative name. ac_srcdir=$ac_top_build_prefix$srcdir$ac_dir_suffix ac_top_srcdir=$ac_top_build_prefix$srcdir ac_abs_top_srcdir=$ac_pwd/$srcdir ;; esac ac_abs_srcdir=$ac_abs_top_srcdir$ac_dir_suffix case $ac_mode in :F) # # CONFIG_FILE # _ACEOF cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1 # If the template does not know about datarootdir, expand it. # FIXME: This hack should be removed a few years after 2.60. ac_datarootdir_hack=; ac_datarootdir_seen= ac_sed_dataroot=' /datarootdir/ { p q } /@datadir@/p /@docdir@/p /@infodir@/p /@localedir@/p /@mandir@/p' case `eval "sed -n \"\$ac_sed_dataroot\" $ac_file_inputs"` in *datarootdir*) ac_datarootdir_seen=yes;; *@datadir@*|*@docdir@*|*@infodir@*|*@localedir@*|*@mandir@*) { printf "%s\n" "$as_me:${as_lineno-$LINENO}: WARNING: $ac_file_inputs seems to ignore the --datarootdir setting" >&5 printf "%s\n" "$as_me: WARNING: $ac_file_inputs seems to ignore the --datarootdir setting" >&2;} _ACEOF cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1 ac_datarootdir_hack=' s&@datadir@&$datadir&g s&@docdir@&$docdir&g s&@infodir@&$infodir&g s&@localedir@&$localedir&g s&@mandir@&$mandir&g s&\\\${datarootdir}&$datarootdir&g' ;; esac _ACEOF # Neutralize VPATH when `$srcdir' = `.'. # Shell code in configure.ac might set extrasub. # FIXME: do we really want to maintain this feature? cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1 ac_sed_extra="$ac_vpsub $extrasub _ACEOF cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1 :t /@[a-zA-Z_][a-zA-Z_0-9]*@/!b s|@configure_input@|$ac_sed_conf_input|;t t s&@top_builddir@&$ac_top_builddir_sub&;t t s&@top_build_prefix@&$ac_top_build_prefix&;t t s&@srcdir@&$ac_srcdir&;t t s&@abs_srcdir@&$ac_abs_srcdir&;t t s&@top_srcdir@&$ac_top_srcdir&;t t s&@abs_top_srcdir@&$ac_abs_top_srcdir&;t t s&@builddir@&$ac_builddir&;t t s&@abs_builddir@&$ac_abs_builddir&;t t s&@abs_top_builddir@&$ac_abs_top_builddir&;t t $ac_datarootdir_hack " eval sed \"\$ac_sed_extra\" "$ac_file_inputs" | $AWK -f "$ac_tmp/subs.awk" \ >$ac_tmp/out || as_fn_error $? "could not create $ac_file" "$LINENO" 5 test -z "$ac_datarootdir_hack$ac_datarootdir_seen" && { ac_out=`sed -n '/\${datarootdir}/p' "$ac_tmp/out"`; test -n "$ac_out"; } && { ac_out=`sed -n '/^[ ]*datarootdir[ ]*:*=/p' \ "$ac_tmp/out"`; test -z "$ac_out"; } && { printf "%s\n" "$as_me:${as_lineno-$LINENO}: WARNING: $ac_file contains a reference to the variable \`datarootdir' which seems to be undefined. Please make sure it is defined" >&5 printf "%s\n" "$as_me: WARNING: $ac_file contains a reference to the variable \`datarootdir' which seems to be undefined. Please make sure it is defined" >&2;} rm -f "$ac_tmp/stdin" case $ac_file in -) cat "$ac_tmp/out" && rm -f "$ac_tmp/out";; *) rm -f "$ac_file" && mv "$ac_tmp/out" "$ac_file";; esac \ || as_fn_error $? "could not create $ac_file" "$LINENO" 5 ;; :H) # # CONFIG_HEADER # if test x"$ac_file" != x-; then { printf "%s\n" "/* $configure_input */" >&1 \ && eval '$AWK -f "$ac_tmp/defines.awk"' "$ac_file_inputs" } >"$ac_tmp/config.h" \ || as_fn_error $? "could not create $ac_file" "$LINENO" 5 if diff "$ac_file" "$ac_tmp/config.h" >/dev/null 2>&1; then { printf "%s\n" "$as_me:${as_lineno-$LINENO}: $ac_file is unchanged" >&5 printf "%s\n" "$as_me: $ac_file is unchanged" >&6;} else rm -f "$ac_file" mv "$ac_tmp/config.h" "$ac_file" \ || as_fn_error $? "could not create $ac_file" "$LINENO" 5 fi else printf "%s\n" "/* $configure_input */" >&1 \ && eval '$AWK -f "$ac_tmp/defines.awk"' "$ac_file_inputs" \ || as_fn_error $? "could not create -" "$LINENO" 5 fi ;; esac done # for ac_tag as_fn_exit 0 _ACEOF ac_clean_files=$ac_clean_files_save test $ac_write_fail = 0 || as_fn_error $? "write failure creating $CONFIG_STATUS" "$LINENO" 5 # configure is writing to config.log, and then calls config.status. # config.status does its own redirection, appending to config.log. # Unfortunately, on DOS this fails, as config.log is still kept open # by configure, so config.status won't be able to write to it; its # output is simply discarded. So we exec the FD to /dev/null, # effectively closing config.log, so it can be properly (re)opened and # appended to by config.status. When coming back to configure, we # need to make the FD available again. if test "$no_create" != yes; then ac_cs_success=: ac_config_status_args= test "$silent" = yes && ac_config_status_args="$ac_config_status_args --quiet" exec 5>/dev/null $SHELL $CONFIG_STATUS $ac_config_status_args || ac_cs_success=false exec 5>>config.log # Use ||, not &&, to avoid exiting from the if with $? = 1, which # would make configure fail if this is the last instruction. $ac_cs_success || as_fn_exit 1 fi if test -n "$ac_unrecognized_opts" && test "$enable_option_checking" != no; then { printf "%s\n" "$as_me:${as_lineno-$LINENO}: WARNING: unrecognized options: $ac_unrecognized_opts" >&5 printf "%s\n" "$as_me: WARNING: unrecognized options: $ac_unrecognized_opts" >&2;} fi sslh-2.1.4/configure.ac000066400000000000000000000003341463704647400147740ustar00rootroot00000000000000 dnl Use autoconf to generate the `configure` script from this and Makefile.in AC_INIT AC_CONFIG_HEADERS(config.h) AC_CONFIG_FILES([Makefile]) AC_CHECK_HEADERS(linux/landlock.h, AC_DEFINE(HAVE_LANDLOCK), []) AC_OUTPUT sslh-2.1.4/container-entrypoint.sh000077500000000000000000000061251463704647400172440ustar00rootroot00000000000000#!/bin/sh # SPDX-License-Identifier: GPL2-or-later # # Copyright (C) 2023 Olliver Schinagl # # A beginning user should be able to docker run image bash (or sh) without # needing to learn about --entrypoint # https://github.com/docker-library/official-images#consistency set -eu bin='sslh' # run command if it is not starting with a "-" and is an executable in PATH if [ "${#}" -le 0 ] || \ [ "${1#-}" != "${1}" ] || \ [ -d "${1}" ] || \ ! command -v "${1}" > '/dev/null' 2>&1; then entrypoint='true' fi unconfigure_iptables() { echo "Received SIG TERM/INT/KILL. Removing iptables / routing changes" set +e # Don't exit if got error set -x iptables -t raw -D PREROUTING ! -i lo -d 127.0.0.0/8 -j DROP iptables -t mangle -D POSTROUTING ! -o lo -s 127.0.0.0/8 -j DROP iptables -t nat -D OUTPUT -m owner --uid-owner sslh -p tcp --tcp-flags FIN,SYN,RST,ACK SYN -j CONNMARK --set-xmark 0x01/0x0f iptables -t mangle -D OUTPUT ! -o lo -p tcp -m connmark --mark 0x01/0x0f -j CONNMARK --restore-mark --mask 0x0f ip rule del fwmark 0x1 lookup 100 ip route del local 0.0.0.0/0 dev lo table 100 if [ $(cat /proc/sys/net/ipv6/conf/all/disable_ipv6) -eq 0 ]; then ip6tables -t raw -D PREROUTING ! -i lo -d ::1/128 -j DROP ip6tables -t mangle -D POSTROUTING ! -o lo -s ::1/128 -j DROP ip6tables -t nat -D OUTPUT -m owner --uid-owner sslh -p tcp --tcp-flags FIN,SYN,RST,ACK SYN -j CONNMARK --set-xmark 0x01/0x0f ip6tables -t mangle -D OUTPUT ! -o lo -p tcp -m connmark --mark 0x01/0x0f -j CONNMARK --restore-mark --mask 0x0f ip -6 rule del fwmark 0x1 lookup 100 ip -6 route del local ::/0 dev lo table 100 fi set -e set +x } configure_iptables() { echo "Configuring iptables and routing..." set +e # Don't exit if got error set -x iptables -t raw -A PREROUTING ! -i lo -d 127.0.0.0/8 -j DROP iptables -t mangle -A POSTROUTING ! -o lo -s 127.0.0.0/8 -j DROP iptables -t nat -A OUTPUT -m owner --uid-owner sslh -p tcp --tcp-flags FIN,SYN,RST,ACK SYN -j CONNMARK --set-xmark 0x01/0x0f iptables -t mangle -A OUTPUT ! -o lo -p tcp -m connmark --mark 0x01/0x0f -j CONNMARK --restore-mark --mask 0x0f ip rule add fwmark 0x1 lookup 100 ip route add local 0.0.0.0/0 dev lo table 100 if [ $(cat /proc/sys/net/ipv6/conf/all/disable_ipv6) -eq 0 ]; then ip6tables -t raw -A PREROUTING ! -i lo -d ::1/128 -j DROP ip6tables -t mangle -A POSTROUTING ! -o lo -s ::1/128 -j DROP ip6tables -t nat -A OUTPUT -m owner --uid-owner sslh -p tcp --tcp-flags FIN,SYN,RST,ACK SYN -j CONNMARK --set-xmark 0x01/0x0f ip6tables -t mangle -A OUTPUT ! -o lo -p tcp -m connmark --mark 0x01/0x0f -j CONNMARK --restore-mark --mask 0x0f ip -6 rule add fwmark 0x1 lookup 100 ip -6 route add local ::/0 dev lo table 100 fi set -e set +x } for _args in "${@}" ; do if [ "${_args:-}" = '--transparent' ] ; then echo '--transparent flag is set' configure_iptables trap unconfigure_iptables TERM INT KILL break fi done # Drop privileges and run as sslh user sslh_cmd="${entrypoint:+${bin}} ${@}" echo "Executing with user 'sslh': ${sslh_cmd}" exec su - sslh -c "${sslh_cmd}" & wait "${!}" exit 0 sslh-2.1.4/doc/000077500000000000000000000000001463704647400132535ustar00rootroot00000000000000sslh-2.1.4/doc/FAQ.md000066400000000000000000000132121463704647400142030ustar00rootroot00000000000000Frequently Asked Questions ========================== When something doesn't work, look up here... and if it still doesn't work, report how what was suggested here went. It's also worth reading [how to ask questions](http://www.catb.org/~esr/faqs/smart-questions.html) before posting on the mailing list or opening an issue in GitHub. Getting more info ================= In general, if something doesn't work, you'll want to run `sslh` with lots of logging, and the logging directly in the terminal (Otherwise, logs are sent to `syslog`, and usually end up in `/var/log/auth.log`). You will achieve this by running `sslh` in foreground with verbose: ``` sslh -v 1 -f -F myconfig.cfg ``` Higher values of `verbose` produce more information. 1 is usually sufficient. 2 will also print incoming packets used for probing. forward to [PROBE] failed:connect: Connection refused ===================================================== Usually this means `sslh` is configured to forward a protocol somewhere, but no service is listening on the target address. Check your `sslh` configuration, check the corresponding server really is listening and running. Finally, check the server is listening where you expect it to: ``` netstat -lpt ``` I get a segmentation fault! =========================== Well, it's not yours (fault): a segfault is always a bug in the programme. Usually standard use cases are well tested, so it may be related to something unusual in your configuration, or even something wrong, but it should still never result in a segfault. Thankfully, when they are deterministic, segfaults are usually fairly easy to fix if you're willing to run a few diagnostics to help the developer. First, make sure you have debug symbols: ``` $ file sslh-select sslh-select: ELF 64-bit LSB pie executable, x86-64, version 1 (SYSV), dynamically linked, interpreter /lib64/ld-linux-x86-64.so.2, for GNU/Linux 3.2.0, BuildID[sha1]=a758ac75ff11f1ace577705b4d6627e301940b59, with debug_info, not stripped ``` Note `with debug_info, not stripped` at the end. If you don't have that, your distribution stripped the binary: you will need to get the source code and compile it yourself (that way, you will also get the latest version). Install `valgrind` and run `sslh` under it: ``` valgrind --leak-check=full ./sslh-fork -v 2 -f -F yourconfig.cfg ``` Report the full output to the mailing list or github. Valgrind is very powerful and gives precise hints of what is wrong and why. For example on `sslh` issue (#273)[https://github.com/yrutschle/sslh/issues/273]: ``` sudo valgrind --leak-check=full ./sslh-fork -v 2 -f -F /etc/sslh.cfg ==20037== Memcheck, a memory error detector ==20037== Copyright (C) 2002-2017, and GNU GPL'd, by Julian Seward et al. ==20037== Using Valgrind-3.15.0 and LibVEX; rerun with -h for copyright info ==20037== Command: ./sslh-fork -v 2 -f -F /etc/sslh.cfg ==20037== sslh-fork v1.21b-1-g2c93a01-dirty started --20037-- WARNING: unhandled arm-linux syscall: 403 --20037-- You may be able to write your own handler. --20037-- Read the file README_MISSING_SYSCALL_OR_IOCTL. --20037-- Nevertheless we consider this a bug. Please report --20037-- it at http://valgrind.org/support/bug_reports.html. ==20040== Conditional jump or move depends on uninitialised value(s) ==20040== at 0x112A3C: parse_tls_header (tls.c:162) ==20040== by 0x111CEF: is_tls_protocol (probe.c:214) ==20040== by 0x11239F: probe_client_protocol (probe.c:366) ==20040== by 0x10A8F7: start_shoveler (sslh-fork.c:98) ==20040== by 0x10AE9B: main_loop (sslh-fork.c:200) ==20040== by 0x1114FB: main (sslh-main.c:322) ==20040== ``` Here we see that something wrong is happening at `tls.c` line 162, and it's linked to an uninitialised value. Using sslh for virtual hosting ============================== Virtual hosting refers to having several domain names behind a single IP address. All Web servers handle this, but sometimes it can be useful to do it with `sslh`. TLS virtual hosting with SNI ---------------------------- For TLS, this is done very simply using Server Name Indication, SNI for short, which is a TLS extension whereby the client indicates the name of the server it wishes to connect to. This can be a very powerful way to separate several TLS-based services hosted behind the same port: simply name each service with its own hostname. For example, we could define `mail.rutschle.net`, `im.rutschle.net`, `www.rutschle.net`, all of which point to the same IP address. `sslh` uses the `sni_hostnames` setting of the TLS probe to do this, e.g.: ``` protocols: ( { name: "tls"; host: "localhost"; port: "993"; sni_hostnames: [ "mail.rutschle.net" ]; }, { name: "tls"; host: "localhost"; port: "xmpp-client"; sni_hostnames: [ "im.rutschle.net" ]; }, { name: "tls"; host: "localhost"; port: "4443"; sni_hostnames: [ "www.rutschle.net" ]; } ); ``` HTTP virtual hosting with regex ------------------------------- If you wish to serve several Web domains over HTTP through `sslh`, you can do this simply by using regular expressions on the Host specification part of the HTTP query. The following example forwards connections to `host_A.acme` to 192.168.0.2, and connections to `host_B.acme` to 192.168.0.3. ``` protocols: ( { name: "regex"; host: "192.168.0.2"; port: "80"; regex_patterns: ["^(GET|POST|PUT|OPTIONS|DELETE|HEADER) [^ ]* HTTP/[0-9.]*[\r\n]*Host: host_A.acme"] }, { name: "regex"; host: "192.168.0.3"; port: "80"; regex_patterns: ["^(GET|POST|PUT|OPTIONS|DELETE|HEADER) [^ ]* HTTP/[0-9.]*[\r\n]*Host: host_B.acme"] } ); ``` sslh-2.1.4/doc/INSTALL.md000066400000000000000000000114271463704647400147100ustar00rootroot00000000000000Pre-built binaries ================== Docker images of `master` and of the tagged versions are available directly from [Github](https://github.com/yrutschle/sslh/pkgs/container/sslh). Windows binaries for Cygwin are graciously produced by nono303 on his [repository](https://github.com/nono303/sslh). Compile and install =================== Dependencies ------------ `sslh` uses: * [libconfig](http://www.hyperrealm.com/libconfig/). For Debian this is contained in package `libconfig-dev`. You can compile with or without it using USELIBCONFIG in the Makefile. * [libwrap](http://packages.debian.org/source/unstable/tcp-wrappers). For Debian, this is contained in packages `libwrap0-dev`. You can compile with or without it using USELIBWRAP in the Makefile. * [libsystemd](http://packages.debian.org/source/unstable/libsystemd-dev), in package `libsystemd-dev`. You can compile with or without it using USESYSTEMD in the Makefile. * [libcap](http://packages.debian.org/source/unstable/libcap-dev), in package `libcap-dev`. You can compile with or without it using USELIBCAP in the Makefile * libbsd, to enable to change the process name (as shown in `ps`, so each forked process shows what protocol and what connection it is serving), which requires `libbsd` at runtime, and `libbsd-dev` at compile-time. * libpcre2, in package `libpcre2-dev`. You can compile with or without it using ENABLE_REGEX in the Makefile. * libev-dev, in package `libev-dev`. If you build a binary specifically and do not build `sslh-ev`, you don't need this. For OpenSUSE, these are contained in packages libconfig9 and libconfig-dev in repository For Fedora, you'll need packages `libconfig` and `libconfig-devel`: yum install libconfig libconfig-devel If you want to rebuild `sslh-conf.c` (after a `make distclean` for example), you will also need to add [conf2struct](https://www.rutschle.net/tech/conf2struct/README.html) (v1.5) to your path. The test scripts are written in Perl, and will require `IO::Socket::INET6` (`libio-socket-inet6-perl` in Debian). Compilation ----------- After this, the Makefile should work: make install There are a couple of configuration options at the beginning of the Makefile: * `USELIBWRAP` compiles support for host access control (see `hosts_access(3)`), you will need `libwrap` headers and library to compile (`libwrap0-dev` in Debian). * `USELIBCONFIG` compiles support for the configuration file. You will need `libconfig` headers to compile (`libconfig8-dev` in Debian). * `USESYSTEMD` compiles support for using systemd socket activation. You will need `systemd` headers to compile (`systemd-devel` in Fedora). * `USELIBBSD` compiles support for updating the process name (as shown by `ps`). Generating the configuration parser ----------------------------------- The configuration file and command line parser is generated by `conf2struct`, from `sslhconf.cfg`, which generates `sslh-conf.c` and `sslh-conf.h`. The resulting files are included in the source so `sslh` can be built without `conf2struct` installed. Further, to prevent build issues, `sslh-conf.[ch]` has no dependency to `sslhconf.cfg` in the Makefile. In the event of adding configuration settings, they need to be regenerated using `make c2s`. Binaries -------- The Makefile produces three different executables: `sslh-fork`, `sslh-select` and `sslh-ev`: * `sslh-fork` forks a new process for each incoming connection. It is well-tested and very reliable, but incurs the overhead of many processes. If you are going to use `sslh` for a "small" setup (less than a dozen ssh connections and a low-traffic https server) then `sslh-fork` is probably more suited for you. * `sslh-select` uses only one thread, which monitors all connections at once. It only incurs a 16 byte overhead per connection. Also, if it stops, you'll lose all connections, which means you can't upgrade it remotely. If you are going to use `sslh` on a "medium" setup (a few hundreds of connections), or if you are on a system where forking is expensive (e.g. Windows), `sslh-select` will be better. * `sslh-ev` is similar to `sslh-select`, but uses `libev` as a backend. This allows using specific kernel APIs that allow to manage thousands of connections concurrently. Installation ------------ * In general: ```sh ./configure make cp sslh-fork /usr/local/sbin/sslh cp basic.cfg /etc/sslh.cfg vi /etc/sslh.cfg ``` * For Debian: ```sh cp scripts/etc.init.d.sslh /etc/init.d/sslh ``` * For CentOS: ```sh cp scripts/etc.rc.d.init.d.sslh.centos /etc/rc.d/init.d/sslh ``` You might need to create links in /etc/rc.d so that the server start automatically at boot-up, e.g. under Debian: ```sh update-rc.d sslh defaults ``` sslh-2.1.4/doc/README.MacOSX000066400000000000000000000025051463704647400152260ustar00rootroot00000000000000 sslh is available for Mac OS X via MacPorts. If you have MacPorts installed on your system you can install sslh by executing the following in the Terminal: port install sslh Also, the following is a helpful launchd configuration that covers the most common use case of sslh. Save the following into a text file, e.g. /Library/LaunchDaemons/net.rutschle.sslh.plist, then load it with launchctl or simply reboot. ----BEGIN FILE---- Disabled KeepAlive Label net.rutschle.sslh ProgramArguments /opt/local/sbin/sslh -f -v -u nobody -p 0.0.0.0:443 --ssh localhost:22 --tls localhost:443 QueueDirectories RunAtLoad StandardErrorPath /Library/Logs/sslh.log StandardOutPath /Library/Logs/sslh.log WatchPaths ----END FILE---- sslh-2.1.4/doc/README.Windows.md000066400000000000000000000044611463704647400161700ustar00rootroot00000000000000It is possible to run `sslh` on Windows. The `fork` model should be avoided as it is very inefficient on Windows, but `sslh-select` and `sslh-ev` both work with good performance (prefer the latter, however). The following script downloads the latest cygwin, the latest version of sslh, and then compiles and copies the binaries with dependancies to an output folder. It may be needed to correct it from time to time, but it works. I use it in a virtual machine. Just retrieve WGET.EXE from https://eternallybored.org/misc/wget/ or git binaries. Copy the 3 files GO.cmd wget.exe compile.sh to C root folder, then execute **GO.cmd** with administrative rights. with **GO.cmd** @ECHO OFF CD /D "%~dp0" NET SESSION >NUL 2>&1 IF %ERRORLEVEL% NEQ 0 ( ECHO Permission denied. This script must be run as an Administrator. ECHO: GOTO FIN ) ELSE ( ECHO Running as Administrator. TIMEOUT /T 2 >NUL wget --no-check-certificate https://www.cygwin.com/setup-x86_64.exe IF NOT EXIST setup-x86_64.exe GOTO FIN MKDIR C:\Z setup-x86_64.exe -l C:\Z -s ftp://ftp.funet.fi/pub/mirrors/sourceware.org/pub/cygwin/ -q -P make -P git -P gcc-g++ -P autoconf -P automake -P libtool -P libpcre-devel -P libpcre2-devel -P bison -P libev-devel MKDIR C:\cygwin64\home\user COPY COMPILE.SH C:\cygwin64\home\user START C:\cygwin64\bin\mintty.exe /bin/bash --login -i ~/compile.sh START EXPLORER C:\zzSORTIE ) :FIN PAUSE EXIT and **compile.sh** # SAVE FILE TO UNIX FORMAT # COPY IT IN C cygwin64 home user git clone https://github.com/hyperrealm/libconfig.git cd libconfig autoreconf -fi ./configure make make install cd .. cp /usr/local/lib/libconfig.* /usr/lib git clone https://github.com/yrutschle/sslh.git cd sslh make cd .. mkdir /cygdrive/c/zzSORTIE cp ./sslh/sslh*.exe /cygdrive/c/zzSORTIE cp /usr/local/bin/cygconfig-11.dll /cygdrive/c/zzSORTIE cp /cygdrive/c/cygwin64/bin/cygwin1.dll /cygdrive/c/zzSORTIE cp /cygdrive/c/cygwin64/bin/cygpcreposix-0.dll /cygdrive/c/zzSORTIE cp /cygdrive/c/cygwin64/bin/cygpcre-1.dll /cygdrive/c/zzSORTIE cp /cygdrive/c/cygwin64/bin/cygev-4.dll /cygdrive/c/zzSORTIE cp /cygdrive/c/cygwin64/bin/cygpcre2-8-0.dll /cygdrive/c/zzSORTIE This method was contributed by lerenardo on [github](https://github.com/yrutschle/sslh/issues/196#issuecomment-1692805639). sslh-2.1.4/doc/config.md000066400000000000000000000153461463704647400150530ustar00rootroot00000000000000Configuration ============= If you use the scripts provided, sslh will get its configuration from /etc/sslh.cfg. Please refer to example.cfg for an overview of all the settings. A good scheme is to use the external name of the machine in `listen`, and bind `httpd` to `localhost:443` (instead of all binding to all interfaces): that way, HTTPS connections coming from inside your network don't need to go through `sslh`, and `sslh` is only there as a frontal for connections coming from the internet. Note that 'external name' in this context refers to the actual IP address of the machine as seen from your network, i.e. that that is not `127.0.0.1` in the output of `ifconfig(8)`. Libwrap support --------------- Sslh can optionally perform `libwrap` checks for the sshd service: because the connection to `sshd` will be coming locally from `sslh`, `sshd` cannot determine the IP of the client. OpenVPN support --------------- OpenVPN clients connecting to OpenVPN running with `-port-share` reportedly take more than one second between the time the TCP connection is established and the time they send the first data packet. This results in `sslh` with default settings timing out and assuming an SSH connection. To support OpenVPN connections reliably, it is necessary to increase `sslh`'s timeout to 5 seconds. Instead of using OpenVPN's port sharing, it is more reliable to use `sslh`'s `--openvpn` option to get `sslh` to do the port sharing. Using proxytunnel with sslh --------------------------- If you are connecting through a proxy that checks that the outgoing connection really is SSL and rejects SSH, you can encapsulate all your traffic in SSL using `proxytunnel` (this should work with `corkscrew` as well). On the server side you receive the traffic with `stunnel` to decapsulate SSL, then pipe through `sslh` to switch HTTP on one side and SSL on the other. In that case, you end up with something like this: ssh -> proxytunnel -e ----[ssh/ssl]---> stunnel ---[ssh]---> sslh --> sshd Web browser -------------[http/ssl]---> stunnel ---[http]--> sslh --> httpd Configuration goes like this on the server side, using `stunnel3`: stunnel -f -p mycert.pem -d thelonious:443 -l /usr/local/sbin/sslh -- \ sslh -i --http localhost:80 --ssh localhost:22 * stunnel options: * `-f` for foreground/debugging * `-p` for specifying the key and certificate * `-d` for specifying which interface and port we're listening to for incoming connections * `-l` summons `sslh` in inetd mode. * sslh options: * `-i` for inetd mode * `--http` to forward HTTP connections to port 80, and SSH connections to port 22. Capabilities support -------------------- On Linux (only?), you can compile sslh with `USELIBCAP=1` to make use of POSIX capabilities; this will save the required capabilities needed for transparent proxying for unprivileged processes. Alternatively, you may use filesystem capabilities instead of starting sslh as root and asking it to drop privileges. You will need `CAP_NET_BIND_SERVICE` for listening on port 443 and `CAP_NET_RAW` for transparent proxying (see `capabilities(7)`). You can use the `setcap(8)` utility to give these capabilities to the executable: sudo setcap cap_net_bind_service,cap_net_raw+pe sslh-select Then you can run sslh-select as an unprivileged user, e.g.: sslh-select -p myname:443 --ssh localhost:22 --tls localhost:443 Transparent proxy support ------------------------- Transparent proxying is described in its own [document](tproxy.md). Systemd Socket Activation ------------------------- If compiled with `USESYSTEMD` then it is possible to activate the service on demand and avoid running any code as root. In this mode any listen configuration options are ignored and the sockets are passed by systemd to the service. Example socket unit: [Unit] Before=sslh.service [Socket] ListenStream=1.2.3.4:443 ListenStream=5.6.7.8:444 ListenStream=9.10.11.12:445 FreeBind=true [Install] WantedBy=sockets.target Example service unit: [Unit] PartOf=sslh.socket [Service] ExecStart=/usr/sbin/sslh -v -f --ssh 127.0.0.1:22 --tls 127.0.0.1:443 KillMode=process CapabilityBoundingSet=CAP_NET_BIND_SERVICE CAP_NET_RAW PrivateTmp=true PrivateDevices=true ProtectSystem=full ProtectHome=true User=sslh With this setup only the socket needs to be enabled. The sslh service will be started on demand and does not need to run as root to bind the sockets as systemd has already bound and passed them over. If the sslh service is started on its own without the sockets being passed by systemd then it will look to use those defined on the command line or config file as usual. Any number of ListenStreams can be defined in the socket file and systemd will pass them all over to sslh to use as usual. To avoid inconsistency between starting via socket and starting directly via the service Requires=sslh.socket can be added to the service unit to mandate the use of the socket configuration. Rather than overwriting the entire socket file drop in values can be placed in /etc/systemd/system/sslh.socket.d/.conf with additional ListenStream values that will be merged. In addition to the above with manual .socket file configuration there is an optional systemd generator which can be compiled - systemd-sslh-generator This parses the /etc/sslh.cfg (or /etc/sslh/sslh.cfg file if that exists instead) configuration file and dynamically generates a socket file to use. This will also merge with any sslh.socket.d drop in configuration but will be overridden by a /etc/systemd/system/sslh.socket file. To use the generator place it in /usr/lib/systemd/system-generators and then call systemctl daemon-reload after any changes to /etc/sslh.cfg to generate the new dynamic socket unit. Fail2ban -------- If using transparent proxying, just use the standard ssh rules. If you can't or don't want to use transparent proxying, you can set `fail2ban` rules to block repeated ssh connections from an IP address (obviously this depends on the site, there might be legitimate reasons you would get many connections to ssh from the same IP address...) See example files in scripts/fail2ban. UDP --- `sslh` can perform demultiplexing on UDP packets as well. This does not work with `sslh-fork` (it is not possible to support UDP with a forking model). Specify a listening address and target protocols with `is_udp: true`. `sslh` will wait for incoming UDP packets, run the probes in the usual fashion, and forward packets to the appropriate target. `sslh` will then remember the association between remote host to target server for 60 seconds by default, which can be overridden with `udp_timeout`. This allows to process both single-datagram protocols such as DNS, and connection-based protocols such as QUIC. An example for supporting QUIC is shown in `example.cfg`. sslh-2.1.4/doc/scenarios-for-simple-transparent-proxy.md000066400000000000000000000117541463704647400233640ustar00rootroot00000000000000# Three Scenarios for the simple transparent proxy setup # ![Simple Transparent Proxy Examples](./sslh-examples-v3.png) ## Introduction ## The first example is the configuration, which was described in the previousd document. I omitted the loopback interface "lo" in those diagrams, trying not no overload the picture. The connections have two different endings, showing the direction of the opening connection (SYN flag) and the answer connection (SYN-ACK flags). This is important, as the traffic in the transparent proxy setup flows somewhat unexpected. ## Example 1 ## The first example shows the setup, which is described in the [previous document](./simple_transparent_proxy.md). You see the Client connecting to sslh (red connection). When sslh accepts this connection, the SYN-ACK packet is send back to the client, which sends the first data packet(s) together with the ACK for the SYN-ACK. So the bidirectional tcp connection is fully open. Sslh opens now the blue connection to sshd and needs for that elevation rights, as it uses the clients IP address as its own address for opening this connection. Now things are becoming complicated: Sshd send back the first packet with SYN-ACK flags (green line), addressed to the clients IP (dotted line). As already described, that would go wrong, so our routing trick makes this packet beeing deflected back to sslh, so this tcp connection is also opened. But we have here now an asymetric behaviour, that the read and write pathes of the tcp connection are going different routes. The sslh process shuffles now all the bytes coming from sshd from the green line to the red line, vice versa for the packets from the client. ## Example 2 ## In this example sshd is running on another server. No matter, if this is docker, kvm, virtualbox or another physical host, connected with an ethernet cable. Here we need no dummy interface, so we need another way, to configure our routing deflection. The principle is the same: We need to force packets coming back from sshd going to sslh and not directly back to the client. In this case its your decision, where those rules will be tied in, options are: * the startscript of sslh * the docker or kvm configuration * the configuration of the outgoing interface Its two lines you need: ``` ip rule add from SSHD_ADDRESS/32 table sslh ip route add local 0.0.0.0/0 dev lo table sslh ``` On the sshd host, we need no additional rules, as all traffic is coming back to our sslh host, because this is in this setting the default gateway. The only thing, we need to do: Assign a unique IP address only for sshd and all other services, you wish to hide behind sslh and host on this device. There are two ways, how you can add multiple ip addresses to one device. The new _**ip addr add**_ supports multiple add statements to one and the same interface name. So you can just duplicate the interface stancas in the _**/etc/network/interfaces**_ configuration. The problem with this method is, that some older managment tools, like ifconfig are unable to show the additional addresses. So when you are used to some older tools, you may configure sub-interfaces like eth0:1. However my recommendation is, migrate to new tools, get used to it, as old tools don't show you the whole configuration! ## Example 3 ## This is now the extended version of the previous example. The target host has another path back to the client, as there is a default route to another interface. Now we need **TWO** routing deflections, one on the sslh host, like in scenario 2, and one on the sshd target host. The routing setup on the target host looks like: * Add an routing table name for the deflection table in _**/etc/iproute2/rt_tables**_ * Find a location, where you will hook the two routing rules in ``` ip rule add from SSHD_ADDRESS/32 table sslh_routeback ip route add default via SERVER1_ETH1_IP dev eth0 table sslh_routeback ``` This is setting up a default route for all traffic, originating from the ip address sshd (or any other service) is using, back to the host, hosting sslh. On that host, those packets will be deflected again with the same rule from scenario 2. ## Modifications ## Now you can think about many modifications, but the tools will be the same, for all other thinkable scenarios. You must always make sure, that packets from foreign hosts, will find their way back to the sslh host. So if the chain consists of three or four servers, all need the deflection rules. ## Important Finding On Routing ## When I went ahead and wrote in my first drawings the warning, that the kernel in scenario 2 and 3 needs to have forwarding in place, I finally tested, that this is not true. **Both scenarios are working without kernel forwarding beeing activated!** The background: The deflecting routing table cames into the game, before the kernel has to made the decision, that packets with non local ip addresses in source and destination must be forwarded. After the routing rule deliveres the packet to sslh and sslh rewrites the source ip, the packet is treated as local, and can pass the system. sslh-2.1.4/doc/simple_transparent_proxy.md000066400000000000000000000236311463704647400207550ustar00rootroot00000000000000# Transparent Proxy Configuration Using IP Routing# This documentation is another explanation of the transparent proxy with the goal, beeing secure and minimalistic. Besides this documentation will explain, how and why this configuration works. The explanation will only describe the connection to sshd, so the target sshd can be replace with any other target service, sslh supports. ## Introduction in the data flow ## This chapter can be skipped, if you just like to configure things fast. This chapter is a little excurse to the dataflow. First point of all is something, which you will unfortunately not see in the nice routing diagrams for iptables or netfilter-tables (nft) like: [Iptables at wikipedia](https://upload.wikimedia.org/wikipedia/commons/thumb/3/37/Netfilter-packet-flow.svg/2560px-Netfilter-packet-flow.svg.png), [Netfilter Flow](https://wiki.nftables.org/wiki-nftables/index.php/Netfilter_hooks#Netfilter_hooks_into_Linux_networking_packet_flows). Packets from local application talking to other local applications are routed through the loopback-interface. They leave postrouting to lo and reentering from there prerouting, without passing ingress/egress. This has nothing to do with the "**route_localnet = 1**" trick, which only makes, that the the local ip range 10.0.0.0/8 gets routed! As you can read in many articles, this is nothing you should do, as you may bring your system at risk, because it allows to leak packets from outside to applications, which feel themselves secure, by using those unroutable addresses. #### A Simple Simulation #### You can prove this behaviour with a simple test: ``` # In one terminal start socat as a local echo server # this is simulating sshd socat TCP4-LISTEN:2000,bind=SERVER_IP,fork EXEC:cat # In the next terminal start another instance of socat, # simulating sslh socat TCP-LISTEN:3000,bind=SERVER_IP,fork TCP:SERVER_IP:2000 # In another terminal you can watch the traffic on lo tcpdump -i lo port 2000 # In the last terminal talk to the echo server telnet SERVER_IP 2000 ``` You will see your traffic on lo, but not on eth0, if you retry the tcpdump there. If you setup sslh as non transparent proxy, it will just work, as what we have seen. #### Going Transparent #### In case of transparent proxy however, sslh uses some tricks, to reuse the clients IP on its outgoing interface to sshd. It opens the interface in raw mode, so it either needs to be started as root and drop privileges after binding, or you will need to give some capabilities to the sslh binary (cap_net_bind_service,cap_net_raw+ep), if you will start it as restricted user. In this setup we continue, with dropping priviledges. Doing so, you can send packets to the sshd, listening on another interface, but, the answer packets from sshd will get routed back to the client. This however will not work, as the client would refuse those packets, because they don't belong to a tcp session, the client opened. In most cases those packets would even not reach the client, as source ip addresses from private address space, will be blocked by most internet routers and connection providers. So its mandatory, to use some tricks, to get those packets back to sslh. All configurations, I have seen so far, are using two components for that. They bind sshd to lo, and than they introduce some firewall rules, to mark packets, originating from the sshd port on lo, so that those packets can be routed in a next step -based on that marking- back to sslh. ##### Drawbacks From Using loopback ##### This idea has some serious drawbacks: First, you need to allow routing of the local address space, 127.0.0.0/8, with kernel configurations. Search for the string "net.ipv4.conf.default.route_localnet" and you will find lot of articles, why you should not do this. By allowing this, you need additional firewall rules, dropping martian packets, which otherwise would get routed to the internet from other applications, running on lo, not aware, that their traffic could be routed. You need further firewall rules, blocking incoming packets to loopback addresses, as otherwise some applications (especially udp) could be the goal of some bad traffic. ##### Using A Dedicated Interface ##### So this configuration makes use of a own interface, just for the services, where sslh should hide the traffic for. We use a interface of the dummy kernel module, which was designed just for this case. It is an interface, beeing there, having no cable connection or whatsever, but applications can bind to it. We assign to this interface just a /32 private address, as this interface is not part of any network. Doing so, we can avoid all the hassle with marking certain packets, coming from the single applications, sslh has to hide, as we now just route ALL traffic from this specific interface by its ip to sslh. We need one routing rule and one routing table, this covers as many targets sslh will serve on this interface, without adding additional rules for adding apache, openvpn and others. We need no firewall rules, preventing martians, as this single routing rule will deadroute all traffic from this interface, if sslh is not catching it up. We only need firewall protection for this specific ip address, when we have activated ip forwarding on that system. If the system is no router and needs no forwarding, there is no protection needed. ## Finally The Configuration ## As described, we need as a first step a dedicated interface, just for the services, sslh should hide. Its possible, to generate individual interfaces for different configurations, however, that makes things again more complex and has no advantages seen so far. ### Named Routing Table ### As we configure the needed routing rules in the interface configuration, we need to define a name for the sslh routing table first. Using named routing tables helps, understanding the routing configuration, as a name indicates, why this routing table is configured. To do so go to _**/etc/iproute2/rt_tables**_ and add a line ``` 111 sslh ``` ### Dummy Interface ### Now we configure our dedicatet interface. In the file _**/etc/network/interfaces**_, we place this entry: ``` auto dummy0 iface dummy0 inet static address 192.168.255.254/32 pre-up modprobe dummy ## Attention! with kernels, not automatically creating a dummy0 ## interface after module loading the following line should be: ## pre-up modprobe dummy; if [ ! -e /sys/class/net/dummy0 ]; then ip link add dummy0 type dummy ; fi post-up ip rule add from 192.168.255.254 table sslh post-up ip route add local 0.0.0.0/0 dev dummy0 table sslh pre-down ip route del local 0.0.0.0/0 dev dummy0 table sslh pre-down ip rule del from 192.168.255.254 table sslh ``` As long, as your system has no other interfaces with private address-space, or is routing such addresses, you can continue with the given example. Otherwise you need to select a conflict free address. If you are updating a older current configuration, make sure, that you have no longer insecure localnet routing in place: ``` sysctl net.ipv4.conf.default.route_localnet sysctl net.ipv4.conf.all.route_localnet ``` should both report "0"! ### Explanation Of The Routing Rules ### The two routing rules in the dummy0 interface configuration are the key for this configuration. The first line is an routing rule entry, routing everything coming from the dummy0 ip source address to a special routing table _**sslh**_. The next line generates this table implicitly, by inserting a single rule, routing everything from that ip address to dummy0. Opposite to other firewall based configurations, we have those rules now tied to the dummy0 device, dedicated to the hidden services. When this interface comes up, the routing rules are making sure, that no martian packets can leave the system, by some processes using this IP address. When the interface goes down, we delete those rules. Also the startup script needs lo longer special treatment for the transparent mode. #### SSLH Default Configuration #### And finally you need to configute _**/etc/default/sslh**_ with the right settings for all the services, sslh should work for. ``` DAEMON_OPTS="--user sslh --listen SERVER_IP:443 --transparent \ --ssh 192.168.255.254:22 --tls 192.168.255.254:443 \ --pidfile /var/run/sslh/sslh.pid" ``` #### Systemd #### This setup is now startup agnostic. As we don't need special treatment in the startup script, the sysV based init scripts will just work, like the systemd scripts. Nothing needs to be modified, when going transparent. #### Remote Setups #### This concept can also be adapted for several setups, where the sshd (or any other target service) is running in a container, kvm-virtual machine, etc. Precondition is, that the target system is the next hop and uses the sslh-hosting system as default gateway. In addition you need to bind an additional ip-address, solely used for sshd on the corresponding interface. Than you can adapt the routing rule, routing traffic coming back from this ip to the sslh-routing-table. Its also possible, to forward to an next hop system, which has its own default gateway back, bypassing the sslh-host. In this case, you need to add a special route back to the sslh host, for all traffic with the sshd source ip address. This can be done similar to the two rules described above: ``` # first define a name for the table in /etc/iproute2/rt_tables e.g. sslh-routeback ip rule add from IPADRESS-OF-SERVIE table sslh-routeback ip route add default via IPADDRESS-OF_SSLH-HOST dev eth0 table sslh-routeback ``` The details are depending on your network settings. Als long, as the forward chain to the hidden service passes systems under your control, you can add backroutes on each system in that route. Precondition: The used ip address produces no conflict on those systems. [I added a second document](./scenarios-for-simple-transparent-proxy.md), describing three possible scenarios in detail. Those three scenarios should cover all setups related to transparent proxying. sslh-2.1.4/doc/sslh-examples-v3.png000066400000000000000000007223441463704647400171100ustar00rootroot00000000000000PNG  IHDRQ?) pHYs&?tEXtSoftwarewww.inkscape.org< IDATxwx9'{"X-b{AQEouQKkj,jUDm5bE2NoO&Hrr]9'$N^jEDDDDDDDDD&.@DDDDDDDDD EDDDDDDDD$W&H(L\Q("""""""""0QDDDDDDDDDrEaD"""""""""+ EDDDDDDDD$W&H(L\Q("""""""""0QDDDDDDDDDrEaD"""""""""+ EDDDDDDDD$W&H(L\Q("""""""""0QDDDDDDDDDrEaD"""""""""+ EDDDDDDDD$W&H(L\Q("""""""""0QDDDDDDDDDrEaD"""""""""+ EDDDDDDDD$W&H(L\Q("""""""""0QDDDDDDDDDrEaD"""""""""+ EDDDDDDDD$W]?%%%f2Q٬ܽ&ڵIxbwAyt--t=P]H2XV;ڱt7 f;r\]H[~1|:|,N͢+qssw9""""""""".;w5;=1|\۶&ػ|c0_0<ÝCumXWcU""""""""".a-[h^C}Krq{"""""""""/&ZfL&{ ]J.CDDDDDDDD$_%LW8f{!""""""""/&H(L\Q("""""""""0QDDDDDDDDDrEab.ADDDDDDDD$9ػܘ8G/~pI))LJ ,2`h8 >HA(3c&Wsh1b${̜Mrj${8#EFF ~F }_Хlr.f]Uz&u V4cQDDDDDDDD\vEgYm'7aPv}?C&~bkG7x:UmjdQL|jPAmi^U,iA!) hۆJѕx+-Ng.DDDDDDDDDݗ9?>Cʕ(ډcqt,瑶->٬ XVF_O7K3Go1[}p{;V=;棼6%G  qBW8g&""""""""b?v 8 Y:H1O[uYr-lfpO-faPrE}I8;:2ռ |=?DjZ:ɩZv1S{>qX柣U"img5I]C1Ge ;,""""""""R&n04mխICb}g #slXmMLҥOģvs֬r x|)ݛ4dbXZ\̞^LC9Ժ\XK,@9#v_DDDDDDDD5LOL rrpMȦ vT,] @F?֗N&䫑#0a:ŲD]l@\puvpÙeMek:vƊέ3綟HakNt#oNb2/0/$HIKle`$0!Z܉#4,DDDDDDDDDîab1gd4Qsuq` I՚Ʊ-}͇\?l>L55oy|’}-p!Q>zU_f6ۮgX,l>p0[{owwmv`)]|Zoa7IMOi[3+V-ͻYE)Hfי^y#Fо N|.)8;}}e aOf>S]:Na7gg&sR4"-̚]{)["zu[ ɉ'>n )%>W>_q2$53{◴_=w'r|DDDDDDDDD L'[+fիbnYDw?g7qH&'&voҐ6ӲJdUv&VjD6DlchglNΝH>7aS86p\DDDDDDDDD`vǿӹu+ba[&ZE.EDDDDDDDDuDɝ 2]Ģ.Ԥ4g?FDDDDDDDD0)L,,V g,d]:{#""""""""0"7gc@~N S)1IYxV }KWrqp#"{!'}Մd{!"w _a2DSxpđ..]]HINM$:ػ,v\0QniԸn.ED21DeH0QD$wػ li):BÂ)i2D.u"ǎw"Ri;\%.CDDDDDDDDx[++< .GDDDDDDDDbwT~~1q,=Rx-󹒔z˚i@-q FPZ`0ޓgcʏKǓnOgwI""""""""rc9?4:|@ոCs5!Oq /=ǎe#IF1ӜoCE委8qXa 1!јf?%0ٙJviYhyy1BB:7K0?{>?|#Gb ,H c""""""w;eϫ9~|GF_$gFw_='r,};ekc9t&%KP<(ȥ jWti w"""""r+3? KWٷ f瑣7{?:u_s֬#)%!b+Y!:*_iiX(dsīٻ;8:9ڻ;«ώ$rA}yiKtWXh%OX0ſ.=."""""R03{/\J%n)HXm\Y,ذ6_abiW6hV+,%ڹ,ui4"\"p)V{=p,N %JRJT]dÑٲ~ M~_D)Z!L}٣\;#Lpqr=xwSSs|ÝA}՛Ѡ. X- ydj!4ëb<&sq% u%}~~~x;Abb"qqqDFFe~;q?{V=HͮۮzmTmm㶛 xxkjj*SFT\\o^s&"""""E&+s|u߈a\\+Pi'N`}Ixy  Ъth8!>> vC?Pd)== ooo{tquu% +ҦMΝ/Ѐ/U)t3WGc'ri@"""""R80qɘ.!G۵>gg,3y5 `]ʔЧơC!$.UvLev[IOKgɜ-SAiin>`РA|W,~~p_̟Vׇ'Yf4k&~ƔEP .ȶؾi;Ͽ6A eC˲絼8%ptt$T~\Ə.n8;|~ٮ]Kscˆ Ņβh")_O=E侃9򉈈B`=C8}d/C~q"bg^if}Q`0зo_Lܸ)iѮ9[myqȋ,檏?,eSqswo&m^ʦ2|0Fc?|[/Cfڷt [7nJll7PRyF;Y?jk """""Gaba01Ξ YW8km0ΛE ;EVn`Zٿc?-ZwsrrM6.H㸹Ċ+yhW=M5㕧_e%$%&e7--IMߠG02kwqqf<>qV+$[6lՕ~d4iلAũפ>Ń1Y|mV.Y@뎭q&#rA|W7W>ԨS־xPq| UW!r,sf| ӪC+~X= OFum}Hѡ0c{prreepq"/[9}ԕd_we w)wp9K)Ry͗X{ o6ml{'Dǰe65[1/Z{o,ȃc2ٷ{Qg=[/Q<(^&:rl^.=:ۮ-Y]SllL ?s_']]I n]cy >>~O`[c4;3=sIb;vvH`]#.ၧ'W(].{tzcޱ5װuV;¹zo+3̸n\=)\vd2ҼmyWfLۈOЁC~{9溵ˤ&ӵպ4"""""R4(L,dǰy3ֵk1vZ5,za<ïb3p.G#;SXrr.G$͍k).BHXt4evo͜󑞔TXN8Ť>i]_?N͘aTQ{vZz=+1N%eÚ_ٰכr^m*q{DDl_1/bKDDSX Ckƙ3ɨ[bcB۶ayLoa?Cjv6pG-{߯ CewGKFPmW<8>HZȰeVW*o +_tj5x㙸]~{bJ[~]zd]VZ:v@FuoZ(p{DDb̙3?OX1?Z.h FUr߇gy35z-^߉PDI+:+1MۮY3֭dg߫h$""g01no<=1ΟY 񣏰6o^=ᅬa/ZDGRRX,̃-2]\\}xzzt*aa[1?_._LʄёM7Сk&[vǵd;H*[\qED-Y_~FZZ*=G@@ߔ2n6=Tŋ̛%C|Bʆgtl}gΞogי&%'qp$OOf1M={*/^`Ѣ ǽ?K.\l!T&"".CB8|,=>*Z5Cnjһ7ǰkO?}0zj w)w<9s QQQ\t˗/Cjjhggg̥lr` "88ҥKYOTD]?/ZHzcr0]mX۲R%׸#6&_{^KƠ^HOKgɔ,]2Yf^KWrV5+׸keo綝Lxs%\!";6ɔ|gtևJzrZ@ΙCC+P~VС#ڭ9{F5SZ4oGJUlJ bbb:vvO'9q<=^;ۛf#G 6&'00Lxzzq:ueC)fkFleJ/Ibb݃jpttVׅ Q>^^Te Nbĉc9'ŹpԴTb.Gs,V gNsuq/sfztERӲnZ9:*ݏK֭iTʮ;0өZL}}?{.fٲׯ\'dؑ0k–-ZbiԈNM<$''s1=ѣG9}4D`` RX1 C\Nrr2IIICll,lذV+ŊB ~"o}eŊqI*mfrrcƾΦZ:Νө]{yʕ+')],N?/૯q-%kAX&|0?8dGG_dۖق3ZĤD^˜w?q㖶6]73u!?aٲt}KǏdmGGG*'99O2xxxbZ())@oʛhر ԃ2eأO;o/˗n`0ٴI|6m"} ܏fY|7o>qzjÆ_WӪe&Nz?Ogɢ_b{#7?|4y&9kgSnh0lB˜߳GzxYdS7LfybPu+l$} u4/>WQo}߷Z$`޼Y[9/""yg >1^]+tmX^e1<9s~n~ |޽/{޸m.z劂HIIalٲcǎA*U0`*T.u9;;S\9ʕ+GfͰZ?r֭[գA9",˔bһsrlc_I&Y a3yW9vqOoO&Z4S}Dtןg%`͚Fᑹ`ȲtzuDG_b of!/_^=d kL^j֬ dbb/bw&@HH!!a>'Y<}mih_'ۿfL&[%&Cj~9l$ zYa˖/dmן{UBC3rssnF>g%"r[ey|1 BY;v6Tµ6:Tx ,L.c; 5jBmYdzvZ~WV+<3 %Kdɒnݚ+Wk.~7VZEJh۶-UT) FXj=̩BhroRz]wO*Ӻl۸]wVu6xze{0鋉xgO Fwi~ҪC+֯C$'%EzhܢqP?ZuhIX{NDrge,Ymc4mA8;t7O=<=1Wl)?txmF3^Y fӥs,z.9k*F9>'!`/'NP<۞aa:eoA)UW2Ræ}NjjJxbXdz ŊqI*W^!ڷh\HgϞr#]]ݲ} DD$D7k׮eN:EM- 2-[{r88`|-kcyq, e9`ȝ Օ+WXd [nۛN:QN@+oooZlI˖-9y$ׯgʔ)*UnݺQjU{(rWrrrmD[F4n٘-ߴmR /V<(v庽]zvKbKxpk}Ev#aϼm_sww=oׯ9>ýeO7o+懏/!!o؇Cg̩ԬQ&&&r݉^bdd9%66//|'sor*m͟U&۪ӛٮpCD^&ՕVZٻ"йs>}2=kg뿫i-Y..](mO̥6+V`ժUxyyѧOjԨqβCGGGj*LBJxᇵYDDTX\Ќ9ent0O f;F, &}8ؖ9{ _UOD̘9]ѢElhެ }-"^ԪURo*Ӿ]g222p<~~4Mj{@ŊѪebb/sjZuddY@h{`ۻjш?5k򦷣W}}ޭ.?Err[u_'0c_zuӤI+RSSpeʔQc 9 :v 8w ԪUeC}=F#~~ԬQ'iH] *Q{/9G]]n}(L&ʆQ|p5'ҮC]{""Ez#٤;{K>X7mе+ƙ3X|jr9N:e&11h4₃&TIOOqrrߟҥKSlYBBB(SL%E]FFsa۶mt֍FvB̘14yJο%gbO ko}́عaz`M`U@]HXYL^j;ޅ=ncy(""*E֬Ƥw)"Ritife0KڵkDFF~"##IHH͍`BCC_>ッCV+W^%..8bcc9s k׮%66GGG*VHjըV_R, f"22CR|͜+jy믙4i/"Jm?3(Lus&XҤ +p{XV:͛ٻw/aaaiӆJ*pd.ۛb4 N'9z(dҥ̛7ӨQ#j׮]E߿Cwggg Y:vß5kRfMV+'N`ǎ̛7yѸqcڶmO8z۶ml޼YA888駟2}t^z饻Qx)^X}esbh0f׳j*RSSiذ!57d^8h؊?3'}Cjp2g0Fٽ{7 6lqtnb||<~-mڴB 7H8q"+Wcǎ.2}tN:e|Ȑ!g>11OT%]Bad\ܬ~4k1&tH;hҤ M6Nˋ 8eL W{al۶N:ѪUB?M۶m u;ڵcŊ4k www{tXb0f"""IIIL4={*L24Ԯ]5jqF-[Ɩ-[x )B a6mD޽1#6lȺuذaf'~W2f;W#""""""w3%#w@+V`7T짞ܹs 6>}z&͛3rH0a+Wjd2^cU4=ʁHIInϳgok<шjȑ#?~ODDDDDD-  ;{W>j߿`Zw)EZZZ< cǎ~c޽Zjcǎӓ#G}vׯ%JӸ#Gdɜ9s={ɷ~_OKDDDDDDr rL:{ORuk.-[G}K_ {""""""k RV/G2tP/MƤoۆU+Ǝ1"CUx纭3ӓɓ'r-/ʢ1 +VޥyիWgɒ%>}wy/Rn]6c&LԩSOw}܎ȽJa]jժU޽AQd붳㏙˞l^=R+9̐!C0 L>=ϳrr}q1RSSϻYdd$ػ"-==v27̚5￟ 6ڝ;wȜXNFŨQ%S777zj< 8ܼX9BTT (S +V#}8qECQ\eϖzp|Qvd &SoV|=F_Z8,͍2i$.]J.]7~}ӧci:O'G#Ӽֳق .~:``(Q%KRvmJ(A`` >>>Kҵk<Imb!$$:uJ%nzbj%..sq1ϪUvԯ_?'懋/zjv%ιTL^qrrGGU?>u}l}zȍ(L,'NO?ݝ^xᆳ '<7cǎ*Rjj*_| 0,KBUΝ;%Jzl6W0iIF$<<<0~s=99 ɄsVe$%%b4qu ʩS'HKKlHNKMMl6u+7v| F7^I""ri̙3|gԮ] z)/G}ĕ+WrlweK֭5̱zy>{6 0lߎcWηoH&MČ3GeȑtЁJm."""S3f`߾}9X,,_ &`6y4hPd0R /}eϞ=7?HLL?'&&#F]㉈ ;Y x;6#9x0uիX5kva̭CvA՘,m^{9L O%%%ҲUMu۬^ ma%ǧϘ'{ggմjS婮ݔnΜa&#Ìl.DD,  ȵkט6m˗G9::2p@>}ZjŋwQQ]_Ɵit(X(j]bcî1Qi5{%XػF R Ly?:fu4緖k{=wð眽&̠ 07Gѹ3!+Ký{3gk֬lٲL2#F奭;yAuu븠PhqH$vJ Xv-h4D-Zĉ'֭F;\)HQ_|X{=󶢢7o|g|k  ﯌g: ˺8}vL:6++kzIJt/E\c XR۱cp<|БL|}zNɴs<}:S˩S^ܼ'E)^ IDATs(WCW *A\Hn݊Zw9 CCC駟 usj/ҦMBb\r5j@R֌5Qg̀lu=u,_3={ E!kkJ.˖kC0{l/^Lʕ;vlYɄ HJJb…yn/;w0w\9BϞ=9rH$ RtQ! #F|=}1L>槹3IMMstޒ{w輶3'eLObbǏѫO;""´6mYͶГat?UtZj!?&:: Zy3lp1ӧ3fN&::1Ig 9r OA(gϝIO^ms|9r0dzytmΑ`Et{dk.eg7RSSXd& 1 FL3FΝ;RJ_ׯϹsػw/o.\%J4Ov?F5bo7 2RiР~~~7)AG:YfY ЩSb׀Ɔ1cư|r-ZĘ1c^{%<ޚЦM'ߏRљe055 G_T*ѱ?35=7YFODΟ?5kԡ|yg,;oeQ*>Ԏ_g?c?3۶YnFX[j޼¦ {*:6bt[pp Kl re < Nr"֯ Jw(4ĉ#4l gLDR{ͳ9)){}]vו}xa/ף-SS,GMbȰ8{:?Y{)~ЬikãG1ږk">D=z*۷PJiӆիWӶm[~ ˋSy֭Csfddzj"##'-Y~=5cǎEN5j .dٲe3&jׯ̽{puueҤI8;;dT}Z9pÚⱂWpUa;ܿ5kbccGiܸ% 7馜ij~;+ӜeTgllAcqWdees̥T*H$>}1^=}ȓ\񽜘 <&M|۫&{6JD"E&#[׉Ě;:'e[R|뵕+xonM$rʕ}D}K/s}Y?]zk=~n``@'dd<&]\\Dxcbd 5LԳ ){TR;;;=Jy!M6B17'kZdǎEѰ!Qis%''|r7n_RE9i9l$űrJ<==С^,LFFF`֬YC@@@͹/9iĩ)T*cϞ@h]ʵa^^5𨎻[J$[|7ϿOq~Y6=~cMqvĦͫtj: <}ٳ'x,۶GbR6hWJÇ:ɶ( ]I*;?ϢQ՗zr,qp(q Oⱳm.((emq@a 1OdoIs}|ZsҗdrJή JOC续{ ŝH&QLL ofх~o>իGVV T{R%{sn۞g͂%==K; j33N(ODIKyKnţRd)S={y)QCe޼y:tVZEXXׯ_ڵkSLZhAڵ,갅h̎MYx**`/#AD_ABgllKz~cD15S!k7{U*Y0rT?RSStޜ?etV?)eem/>{VܻwWWܼ{~Q^9vٵ{36bǤP(.eݻlIPcψLr_A)S,e+ms2n^%9 |㘞'z#q("} #ŋz'@}||ի"$)ݺ![͑{]VVV"++#F=`$1b֜UEۻw/ >!/*vvvtڕ?ٳg3e.]Jhh(cԩL>VZ+á@A3tj1]5==9"LA( vmfps,[lfrJ$@'߭]-V8r \ު{t Xэдi+JqTJ <݈#ٸiNwP7e,iimjjƈ៲j"Y)Ν{a _=\GعȨp ^?addD-_~ѥs/._>ck~h]۞Kf4xǡCrPϳ!O~<^ŋgReNI+z" `ll+nB.Z33֬Aֲ%qPԫrb]y͎;xƍdޟ Bq$ѼG2T>SFQ(]ss |=9> Z+he5-@!~?,KއT._>GBbeJSz (sMJYYce$,55xT-sVV:MK^0&&4hЬ@c_N=gjj#zɺ(Q9ږ_accWagW&䨣c+zN.}>sԌj{eLNjRdm._>U oZyZT*/>D=tteddpa.]DLL ѽ{w~z3x"""}6ǏΎ5kRf>>o\۰С~~~\~ fϞ-PfM˹BFc'MBz57Z˛' R)NNN899Ѷm[߿ϹsصkiӦԭ[7Ϻ8UVVa>>LȽ3gdɒooqTvm8 hԨQQSdKArr]H2{pI35^+"   ŚH&Ibb"zZMPPƆ H$TR*UpmvڅJ~:ilrB4u"?>…p˜qvvC;vDv裏^ݻs=ݻw+100z;w?L+k!׮]nݺEF-$g΀^&Brucv`C竈   BWKCRSS p\\ , ((;駟I"eL4.]?L\\\שz&IprB|HK[\mۖڵkc~g"""h˗/cnn:ukj޽{<~C)2RwOw] ]f&sϟ{w QԨ|hC h4>D*00|RM ,Wu   +d(Jju[iԭ[7o2'R~B:R)cҤI( ͛͛7NJcFDe ܸ،h׮'OԔsAjks92=*OΘpC}QZ݅zٳ(ڴf͐Mt>$OhhʕCPqK'>>;}>   ̌c* PMΝc0rHJ*{苕#GFXgٳQZ42E&ȶo{l :={̒%KHLL,еOO9dyPPR4)z8H$( !313M6=s_V(*MCz89F;|aQѪ/%,K*xT 2DBQAAD=x1uWܹsl۶ ???vL&A* PT={y$T=?WV-&LgϘ7oQQQ^$ BKU)s*Tz8X"w)0u௿ɓN<]W01A{kO>̙3|:}z$,,DBQAA X@&!Jۜ׮]c۶mtЁ&MmwI&H$oߎi:]k*U"38iȖ,Ar2ʍxx56[[[Ə͛Yp!BBD&?&ILL$--P];/_`ܹBB޷nztŊ=[/˓'?VLLR:w2 eC#CܪѱqLMh./'XF&@۔g+   H&ꉹ9z+667Ҹq2\ƍIJJbӦM|'o{Vת| 6E9޻rݻYz5rʹ{}veccGprr*`SciDV ~DrR ޖTGTY'$RG CY+7#2urodbY 3l"xPAAA(>D2QO,--IJJzyj56l|!վ}{X~=&L(Vmudծ쮶AA(LluSnoV(YaÆQbJz4d0֭۞][fffۗK.q qBAVVV=L .u[OO;Mc5?$~V^eIN*_N(FQ!   ',zbll7nȳ+ܾ}4 !޽K{r"}Y &22={JjhժNǿD\c([,!!!,^ZjQn]j5\pFCZhڴnH̙Hb:e [mmy666tE0̙3EDD;T^__u)+V'3g ~X)ʢ-W^/aY+]E   M$ۛÇӹsg$U1$$,,, )܍;Z{JJ ޹nG?>FFF1rHʖ-ˣGHHH`,W-aÆC$Ĝ9sdddpiڵkLw?x{{P(fΝ9jH[eSrMZ&cA2y5jhĄm2zh … u=v_}O>HR^J&Mر:w\m"VѰg}۷_p*TPQZDDQQQ?/|ٟ9u/_&44:~z޾@פT*7QD t.]Ǐ{*tLMW:>}DBPPPٳgDGGsK.hu۶d=b@&!=y/V̧Ndɒ̞=PO;iiio\TbJ>+QBB%&&&E{ 4~0 }@Ox Oʺuejw%`xQ~6nZ_bldy FE0t8 fR~Y62ey q)$&&ί>۶/ƠR\9G֮ޅy =s NFR?,q]t_OD"biiŽ93VXG6oYER6 <3 @N$%%[BuNj4VJ\7|T'cc>d萱i:0y״iݑGb :Jݹr2x?9r?}6%%'͠{cAGd7Q)U }۴c Y~۳ ܀GL-;Ԏ >vLFNt2om85`ۖU^7c$| FRJ S;Qn=/AH$ A׮]z*իWwIƽow~fŧNNNdee߿VoՔ)hC>hNdR@>MqaÆ c| uΝ;ڦ.fhōЊDFý{ܹsQy\ُpq/mױK7>ĮL-2\3jIRrE  Lv{"no[|;k6[!1eYl ի@TIϞ:+f|9~Z qq|ܭ>_HXd>Η~@/ưxm21&&q fI>7C4+5k&,]7dj̮kr{>t [׾+x5jx?ڿcFI100П055#<<=YdNp~,--9,lݶ9?͠OXXXejռ Bx Oj/*Ut'$ Uz!HMkc-ҲE[];o)J}ס9|T*cO3Xm2|61I{~l۾/BT{n''&~UA(DBD&MرcO>wm;W\/!!otvLƬYr Jݴ)Y'OrҒš Ib+ѣwe۶^zxxpM ɑ533R^ I7VBCCIKKrE{Ci MdβhЫ7ܞTH|EdڥY W9׺u0w7s߿S:m<'`UIMM!`DoퟌgȤyo}ѳܬY&+}%tНk ˙cܹ Щ2 IHHx0k,R)RVXرcpsscƌL2H틽=l߾ի3cƌ׾lY֭Kgo:uZMڹ0` SNK||[ EA~c{`x'TvcÇ; F]ٽga$k֬59˥+WdyTsrrƍ8œ-Z.NNN:MYJRl$ '3r9;tg1OA}:aCvko1f\xؘlټ\Θѓ?uŰc&FH~ihކ'03;©3QQ<իvh_MݧH8Y -X3gbXKX#hėjU _N70*Vt䩣ܹ}_NjU9thd7eӰa7ږ0 tLFDăj|ҦuG?3]?;6R^ڵ~/iėP233ɽRn ڝY~{UbZՋA(dӧO^A|̨Wqqqٳ;w )YSF ._LVVnn~Djj*ڵ{֕B%iMHwF|9h4ȳ3gppp|H͚5>8}4׮]cСm5BoR֪v i?`B$jJEd2h.$IcJؤx%% $)AKx܉z.: ?N冑q٭o100ՃY3}13{gҩc,- '&&Zb0ΝzFí155cܸiND9>޵u>U(Ttq| +ps\nRYvZڹ6ͭ #{VV֌)N묬JѩSO CIHxL;fVnRw"2"W67W眜\h٢Q=OLjeʼx#q(8zaԨZۡm)]ڞ-biY;,,,iѢ-eHRO[4oZ֭RRqukeR)rJ:AR %-ao_;woF1|3o԰9Pm œ9s;v,5RJӦM8tPQwG. Y{9i` u9c\zzhTJ׮] )4 UemZ}ݻwcffFVbMn Bqh牟 1sgDbt/XF\:[V֐RHbz DuH    L,"FFFxxxвeK:wLNhٲ%{ĉ~8Ez9q:u.6$&oTnl,WǵgϞlivrr~lݺB $ z#C }e.]D>}2_fr {yDԚَo^46!˲w[9_$AAA H$زe E[KOOgƍxxxUIݰ!YgϢnߢɣGT*sl;СlܸKh0zѣG믴iwww+@ei}ߔk -I#KxΫ;,.!DPAAA( L, 5QUj56l@ЫW'_kkvFHODѠ {"HP(߿?l߾,=j,1JIIaʕ/_???(UN_̻6oɶEҨy*KX[YE|)5R#a IDATH EAAAbԔ#GƦM˄ZfܿAt+$TF|8 et =av+1=;uތG==}K7У4l3bDokb_*V5*̄bH(   (VHY[[3vX/^֭[ѣGmeXYY;\' iْgݰ!GiJ !!!1:u JEv퐤Yy7͗6mЕ">>իWɓNrh=~u/~@ĴD}|TLc*?46MCOBqjUؠX,  XT.]qˉUwHˊ+>Çdɒ%+ƍ]7nPߧnݺ 6y1aιsؾ};_#S6 {H,YBjj*&Mظ@+ DgH4V&=s疶Cʢ&5Y"-!H   %\R:u*iii̟?___uFwe…$%%1nܸ"HER( 477%x*+bfD*U;v,,]|.4!i->)sm-Z&M$**Dh.u:r渾CʢRiH( "&&ECy#GqćrśfOf4w:RxOW,ȣNrJ2K E"̌ɓ'SF ֮]˶mxYf +VdرYmEUVEWWvmW jw7,˔)ôiP(,\|m`g $,Ǔٳg֭^z?}•QhEHbgXX1@PAr_x;]Ŧͫؽ=39{_#썙Yqڷ댥5iiY|8j%=}ҵkWPK6iŢsdl7=k(V̀&OI2Ȳ̦ͫY~Ihhhϴoaf4jR\Ȃ3u2N>/d@(e ?͛αc044"!>=2zԔߗW׆4w:I 'Π{zA L,´2V5j?}q珗fJ|֖mZ&#B_N6YJУS)Eݡ e"(P}?kq[{"2LzTdG;ɓfѩc #;e(=葿?u4`r?"##>7ݻc(J2~P*T})oXkݬ4k sOy33$v)_tI^Wϵׯ>|Ȱ|7{2aauo/͛>;b 4qN%--`hhUuOo}b "$ gggӧ=ݻs$ CCCLLLCKK+sEaJJ )))ABB, +WsΔ*U[> >>N:=U۶HGX³lٲ-[dn޼ݻwpȈŋ6ZZZ$%%D||<$%%P(bŊt''l]%JZj^iќ13&kXV`JcX٤2n=ڲd}%;,@$A:,󼕙LMg[j~AF/ƌhѪ6gϝY֯gA1|<P%۾qttάX#66\⅍M=<=nn2jW\*gEkHl޲ӲE~ioXYZ3lc Y.Nv2Ms6yw8y[6\!Zr MDL/:dPl|O dGFP䄓S+Y 44/^ bccIOO|NOOJ,IPPr\~3b rWDYի Xx";5hSV-jժ@tt4<(III!55===LLLקDXXX`ii;5q|,DnD dD/t_h߹pt EAGo|]у,+!#hmmǃԚL򹁁!3?wr,˓' ʆ >ڵJ ʗx Uy"73@GG7!  B~ď$Icnn,l2ߏŊ}ٹs'Zzdž(vDn ՘1תM^fll1EzJT6me_kMFN0QCq 1"(P r]bOCOO+Pr^WP #>\[..ٻy>C~g244BRz躺zeM^եZ|=evs42H Y~.i$%%e٪$IjAǽgU5I8p Z$uYj|g|?iP^^`l];TcBjjDwU;Ne칓xe\}&\xcSlm|ob̲Kք7}6?K +ʲclI˜t63-NHmV^1u #GQRuQ QOX(dرc?>k֬aСyy0dժUX[[3t< )Uհa۞Q J?ԙ3KGm4jC`GWOu!̛aֵFixmF&eYA^O BQH7f*=zej9a]޽\&dLʘ1ѴkAAϸu*vv%_{ϖ-ڳaoQ%hݺ%JXiJ =gֵw6.]9YqzȜ9S ȘÇ;sOѧt +<~SGYf'Z|+VY'ѤqK?g/Q*5h N:Bkvv% ~?/k3Ov.#h٢=$qIXbkZ gcii]IBBUNNes 'ǟLRN=Nm~ Er֬YPPJ<==bŊ9WT<{+V`nnΨQ>aԹ3R#YⒷ'1r 7RoPJY #Ce GacN(>o)I5wRm J (РY~z-;UEG|7EDQϵ&&f=Gϻ<ϓϐ!cѽ MḳGܾ}++$Isْ5F[[/{ZZTXb Iղu_Nm&Č*UjdĥɩSGQjhpUn߹Vm-mVnre2H\]bblJٲ_55h 4J߸µbQXZXegtt.oMljkkSv}n 3jhiiP(hղ=FΕޤT9'W?KKkڴ@TT$o\Ԭ 3Pf]?R%-|ŋ7RTk?bb^LRe/Wkڍ+\~[{~R^vMLkgek#QؗMA NZYϢro[uhi9yD#-)f |S02 b%YubGMb>qͣO߶R1hpzD&Y_3-\=wb]jL(s,Z2JݡP[2a_9w,V4ڵkiРÆ D"TJ=2=G>DbNi=~]y]"bW.-fٸ7Yl8up/g9 $LPA Hjj*W^"9l>W/rğo#A>f"(6#GC8pkqaį˗1b]tykgKXy,ŖgA!,ez P wH(t6mO߸SF 899 .pi4552dիWWwX\\Px{1yjT>>ݞ/1HACתmLpp0gxԔ,y˱ Kc7S(X2o 5̛aK2+ca89ƨb5O(޼zU QŌƍ06H FzL@ D1Gh" E4`!'D2Q`111?~<9KʕSK.iiiqEܹ4jԨ&{Unȷn!@WI?Æ`ҶTP[~9s)-OF1p.DUB9zߌ4)W1Y!}pB_'8wvԴp4 g='b &eΉdP8d E$A LLrr2>>>xyy)W...)SbvJTTϏ{JJpssRJ(EPb"qWF^=c۳SB ٴ#666|.k ]b@e/h~׬0_˺gEJ!4h$+cy x938o D^y3xhJuK"("(B~DArB$|[u'55SSS LLL00x aYy%QQQxp={F@@qqqBŊX[UTdn{V(PR~[5E ]I3_WC5jؙ80)WC8y_mS˦0y%5F4d3x&   "(EQɒ(Μɺ-uGҔN:ՈLAIwy܎(~SO[ t6 axV,0eVBiH(  Pdd U۞3=/^(a@zѮ\ai"|U,B͈}T[I-3qÇh3g&`kkRQ   d |P=jt&%;w"UysBPȨb,OYk/I$9 wC0a7W`n^c2th<{uyV-YNTT&H m}_iFRZ%2R)U_G{Vl ¥Rr+s035gmxt= -MUqVHH |oG3bD#AAA"( MMs"8Uݺ(E;V}1Eţ 7./q%R@K\P?II$=iit:U_P2p`x$_y _m]Y?/ɲ̕cw3t$&Rk.VigM,XJȗ;xH"%4NI4J$II4m [vm_=/ƠAnOAA$>kz|cCe_ ӽĀHR ܹy'1sxXΛ_$IVK.ɷN+U˚:'va2x&y3hISEL].7a,ל=ϕ+8;[P? xNϪw3\9K9}ub˖Tf#6xMϞ$ӧQN~ĕ+U CBy4-\=={Ѡl7qq5 nn$$0c AAA4`}F3%N lCxRp4;H%k61|S$9q۷N ̏ó)[/ٳܸHӦ/>cǪYܹ:Q\3+:5x`~ 1eKo~1[-[T zDL-OQNV(fBoDD[@=˫ϡ| Sc0uaL37dٵk;WlYKvѵk, ̙ ,[֝ϫ~WlmM e˖ybaa|۹u]6EC3Se6 VE2\O?s {'斍9sr% q:wXnWCHJ|_1XB{U{q\XXq8㥯[7oUc`s3f}X:Xss<<ҼyyΝ{9DCdWΝ{ѣczDz +ڶĒ%͚[==-7%::F|'FF   'ڷGy:R͚ƍCթDC-MjNTö⣐2!rƪBRܾܭ0&&zWn%&zu&?]70y}9Ueq_P$ m*&G;Vm ӛ*T\/!IGE\(MKÆe>zZՖk/0~;[^V/*ieeDxxLY ں`k  wD2Q>,ׯwoT7i̘zNzbnG)-1?v#1~;/ 99 U4# Վ{gx{?Ȭ==- &Iqy@.:9,92QT2Z?*]BY%]EV/Ie +zejK2&!$` (F' zI=)Ç%L~GL-/H˲As˔)-3f'5cz/&Nl弓S ||(QןT)|MAA#9 'FrwGy RZ=xe Q4%Æ5`޼wyXJ/4=ʖ9a0\%U}\T9Bn5tO hiz:ĄǢJXqN\L"JM%1hq*t$_$UaSA"[R@fH76| 4SH%4Hh޼<W3˲헳3A$f[4!<۴iS}}m~/={ANY QGdnoVd~[WT?_cAA!)EqtmϮ9Q;ŁH^tPԫ爁67nRniv>}Vj[uu n~qRN)MkC&Р?P{vp7RfY32jXx\ܞkH t.<r©l ?ep}^<ύevmi#/})/yGlā7Zu9JÆe|УG-ƍGt\=7I<m`hie}IQ=_ݒMu VrlVr-%EO<~hE܅3ڳwc&Od -RA.hyIY(UrpwrUI52`Ccc,ctX531:5jS  Pd{}6ӧOäf9g``@޽={6ŋOѠAػw/SNeҤI>,4oSSSvUj(As\ޫ1iR_X+޿IQ𚕝B{#=xKcNwC*?4W{[Vn .w8%3cn xyMVwH    L̥{ңG$ʕ+ccc˗/w\ÇIRrϋ/HLL||pqN<63!<m?&Nsy7޲7 ԑE;Q_Y!Xۦ2y%=ڳ|S0&!]T{Zm˂  )\ _~$''ӲeK/_cyJΝ;2dO>_~={V.*U"22ҥK{pt+y"I>>zF;TG2](eJASI9⇣9fA8΋bze9~X\'=wMp/#>ѐv&Ncy H =c iݥnXfٰ'/Zӿ ?i\} 1AAAMD216nHll,FFFxxx``BGӧOΝ;ǵkרV".\[oѢEpi||| lޏcǐAPPB2P[2$~߈5_N֭177J*`jjʀϞpttԔϟk.(]45k֤tҔ.]v. f͚/_sss:t?r|޽gggJ*ܹs,̟??|L0KKKY&NNNXYY1ebbb|ٵkWĉs}5HSzTEkJ/̓萔Bjr{16Γy ;*eoƫ&.7g f2K,i'ah;'FkM|5+  EL777lǏ۷/WvǟB/'/͵SBUÅ&S6c|g8EҬt<6\E^y/d D}:IAwJem^̬a8:0o9}>e`,*iAAA>bD.?3m6lllpwwg\pwe޽{@2ey&/_СCܾ}Ņ Xxqu7cwr}ϱ˻sn5|HV|q2(ϗ;̒*c6|-}4H>CY[Ohѣ~ d^AAA&V&悎N~`ժUDGGsINZhф&t:*-VaaV`!4hs-/>@OO= 5k:db:t %]`zYOŧXT%J=tʕyNGG3Kn5sAAA(D21+O?7|ѣG9ugϞϏ$?Ǚ6m?cǎͼv߾}4i҄J7E4i-Z@C#Kƞ={>|kqpp}l۶={dK&v<:99j%,\~;;lDGGq1GD կ_?["Ԯ]K.gL,dSin9;VȑѨT*Nϊgׯ.zl~!ChQgʖ`S4nk׾EOO\+W`„$%|y, XWaѢ.%7ܹe\<5Qy~bz.[rكC!IAr`NP8Tȶˆ/ZtA ^=GƍkӧQ=Kz?e@v}=4tΝg֬1u>lf͚?'N{'9r>m5"__۶uPE̘1+]pΜ M5d2ҡC%6n<0nZq28`6;|t|,zg\oڻaк"5Л.!S﷜^L^c\ 6`Yoوѣi߾"J)A"@ɦM'h۶BNrp!66 EDDDD䡡0F`ڵxxd<#L0f͚q5OjRvg,XpǠ.%$$аaCvI\:t(u!00 b2hڴiaBru1N(ޟO+s@^X`7}Ԧ(4Ӌ#pvvϓ~X mlϊqǪ/GѢƨ둱}'a['gG.YrX,+;%u۬0` iLcxvh7|f742>ӳ+yxrh0.pc7ޘOΜْ…}8>v]eO<&ӓ]v75nB,rl޽qpc 0LL4"E`6Xb`x'˻'_$>.Ogʜ.z$$&#"btj2sȑ-t&"72~*._ĉjU.k4ȃMa ,H`` ǎc„ ,\wX00ƨ xbq5<==\?3uTVhٙuj*,Yaخ];-Z5j]?C7v-SLAS~ ͛7g\pis)㏔`ʑÅ71e^}em ͛,|MFhI|9})keU  qs3U(9ζeVVIr72o'5jF.c;o7,럹b ۭź?YE2JS 3i?w?_"oKזr%*U`br\XyxrP|}HHtwPό.Krȝۃ'%_-"""""01 D޽Yh}e„ Nw޳g Eн{wNXX}~igܸq+oɪU3f 5k֤QFrJ,Yݺug5ɓr.>>޽{[O;M1sѱcG~wBNHLLH"k)d22`@}~q *h`T\(%`ع,EζZI%{UI)*7)ܘl0`c [k_}nL.d~\=>Y%[3:\wЅ@wy٪f~\|Wb||E0-DU_d41z; ?VBn|Kᅢu:u״<&fB^غu+ӧOgԩ̟?MRX1||| aÆ l޼J@@ӦMKۛ)SЭ[7~gK̛7jժŋ/r]ӧӦM㩧駟f͚888sN-ZDrr2C'O?{GXXUTK.p9.\HPPJo͛LgggfϞMfغu+ŋk׮,X3g0gBCCqwwg)Nʃc޼\y;q3x1'lOO`޼> bɒ}^=޲ۺ9f5I$QR1HKCK\pwyM`x~Z~~n<>DLGth0S+Q WZ(C?tZ*wml6QҊHR SRzuFɅ 7o^vNNNуc\ΝquuW_f)F#]t믿dJ2e  `ر,[eʰa2$鑙QlY}]N:.]ʕ+Wٳ'[l`0h:un:^yvĉSZ*SLBdNѢٲ F"lx-н{v'v^OYs%Jaɒ} ЀZf~Ǐ_s9br9'efnM= V'^IǴS'JصE4+cu'83hDCwۻ<͓]``<|3*S֒^t~NJPP{CJLܾ|Yt];7fs~6~q;I3?xEDDDDQcZV{0JNNfӦMl߾ t4i$T$%%e|)q'Zbpn}?;yK3|Ld?V&ٻy̽|^i/m|{""0LKԫWz29&Dݺu[nu6TT)sOh0h&9s̰[)rpڱږLF5*A;t{j鞋aР_3{jԮ] wwM /_m95u .i5rg׹rr1##8HCОx0v~}_[GK0S~}V\ʕ {n˖,Y?kWW'Jtih4С<&H"o ᑍp*BAo_w=me97w <\y!9fl9;|&7'*7)E-JO4XÏ~~t6t;w:F'>,DŒ`2DDD)L359Ęl,^w<ٿxf]4\ZZu6uw1eP|fXZ~.`u+FT3T=y q}=dw|Vf,k ֬YODD ]ҥxz-7{y2 k-"""ro ODg0k&:"?fm%x{Z7:՘)d~~p=uzϬ҈,oszކa\t4TA;O݂ (`D!f ڶoDRRRaa!dnA%]W\f27%sJSr>OTHVlcqz!+(F|$$wQD{>>(d%6l3Uaԫ$:cǎ$%%R\%7j?/ /y:ڶ̹sYtWS\Eڵ.]HΜ^4ߔ+?q(7IuUjRtN8ʟzRhl۷olD`6N'pQ:uz9s*11.={طoW'JUÓ^DIJJd1)4o֚֭:мYk&|ڵJVqvvZtН5kV&~ Ka̎=ĖYl3y'ҴyuMd|}0vWnެG2}03Mnl7|'4iZ5kWд3L&U꺽{wr^Ѩ"" }Gɀ#ZRPpS~JjGF71 '^a_C}rZW"9v}!{ѝgSv%uH;gʮ# F!LFwPu4PxNtWw(].%#aflظ:Dl@GlZh9ɛ7?S}abyN?W/g\!gNT#CILLB*8::1YlaJ\p6m߰{ώ4t W\ϟЩssBBY~?8ux{0s֔4ߍ={"""'b ՊCW74hjdJ ͐N6g ?°pwc KrRly9rl?Ա=t=R}Nfj֬ߒxx{gI("r T2 pf,||1 pSіMݘj-Tw̴#-1Ep)9W3]kʵ. 0|w8?}k?ΜtWA~RAd 18}RBUx|mWs#iٲZe޿PJJ۰L>,K8ݲqyW J9r71 ʕ2 nvrcgлh{sc/ѽgk4G}v|;'+@eywp?2=:1::_|scgjTbFmƍɨpvv~&'&&m~Ы,QJFzcXZ9sxyg+1@uQ 'bX0뗦}HU;jd<8&j`_r?cݻȐhb.μձ;ƓK6pEg۝;}#Hid Uf~<bM5]QaqT`2D)6g/9vAAU~"9O&gr Bbb315%֯ݗfӗ/@XXarc͛/ 1+Qzvݦ_ Oqbڽ5k4aba'-L9rתUgŋᙃz'ra_NˡCޭpch":}<(Nf3111X,6~ #%.\ril':sNpE~y&m&!!CnfL_Q'䩖m1?+W-ÓgZs~9ϴlOņ1 \gmЮmN>AOQD.]o^&6`ߍ-k陃Tor2^>""} [?DDnbu/~^g5:kq1~cGp ^{u2ÒlaPItԔJ|]ȕ:“Sٛt J%tͱaf-5w|;s]]tmrՁɳZ kxwxa>}>( f,_n/w9w}3;7F/=!Gs11 7w 'sq"#)\HMVz5|n;2bIرz5^^>Q*_wu[,ɜ!}vaX(_rA8q?+ZG &''bbdJ~-jM`\Ͼ`RD"bȄ ؾ}#4NNF6lI".)':wNFjzbS,J^+',iրbԂ:;ZDžqa Ϸ$~9<8s +aIل[$>!$ŷ#XKyde$Ee}P .m6S.+fY&6! I 1/J|]j>|ꑌ_$;AT}wA-ZдU,PD/e2DnD!0QD$$[-lG24#cHJ+t^LsM+b,f#Cp58ܵϺ֧c>~;OHh֫&יط8Ϗr{ _ z{ ED2R?w"*R$Q""0QD$[84t=lOk _`v¥2Z3X9^_؝$LcA:wKU $%1WX`7}ǵŋaЫ1.b%6J+0.ADDDc ED']jn}]1O[cY_tRNGj9km*K{6а8m^uW?3.Qu{$"""""m&d#G _CvXLJHݜ8kw7. :<$6Չ*YH^߇̌*Fٻ$Nays0U~^;C}&evU6͎!6װsv3u{)88qtζgyEE:s}_ӢDy<)L3:7p|^F:Ns95`tf's U?>9Ч?G+l QDDDDD_YDQçPPA٣Řvj-WMD]ssof+TŻH|8mnEGy?9WhAA^e<bk1Qq8`GKF{#Mб%>Vbcρ=μ? ;*H""3X`7.c5ekL4r6c"foMc#tˮ.?*{Dڻ$D; $'YRusi֐Uy9d g.٩G_\Ws+\幞.IDDDDD䁢0QDXn+`Ҝs4ّQ^} I'i*m)AVJ$D/˧X`w8W\Ç,rI֤\).HŅ]Q(""""" /%1!Q|sDK :`6e19x^}N?ώ. }./hD(LXwD˗jgx@<)6y;G7:ȹ29{Q\ xL%$xw^mtB[A(LD&GukT)"2$kڸ58d GcDo ]ysXۻ$ޣPD|rp9\è\E00t"]!_ B^CCxU""""""P("!ɁޮgFx]9x>WG+H-d}$&xż_kCD[)L"M5[a@ N ĬeXӱ|vyuy? ƫBFKy(L"-۶b匝DDۭ⎅k%aae%}o<|_(w%%xA⻡DR("E 6~GTxatgxaG!p{خsxwEU_`(/ QDDDDDn(LBUjTWV/}&._K tvo7H&zx~#S}X-?̌KL9rfS'9~,̓oDew""SL)~?ᓿ҅`VNJaieKqfeun*R&-qrrf[#A~=<%<&d<1MK dXeJ)<@CVt$d%""!'b\!Ζ.Ϻeݓ[A/?CH""""""@ac1?& du$ڻLZa!E2b wްZDDDDDD2Iac.dvbn\ࢽ˲  ]""""""IaQQ|l9sl`˺#>OmHFNQ?DDDDDD^rHֆV1I?KXǐDJ 姙9h>U("""""KDDR)a(/4 sx"Wy63|t.]ς$z%""igOi^Ҏ֭.+gs^4oiD_""6 .k7Xص?w_yѬU|ծ}777ׯOB A7o^lҥK9w=zB n,""""""=""vKnnd2ѤI4iaƍ9@@@@ct%ձmfbyiDDDDDDDDDDDDDDD Kx,YDDDDDa0QD&ggbbb]#-::7{!""""""wIaM9spE{Ht9rػ K EDn*X#GoRIIII? ۻK EDn ,&V^mRI6m"..KڻK EDnrtrVZ^ÇۻGʩSX27Lyh)LKPFyf̘X,.fZپ};'ODTQ%=0ٻMqtgٲelٲURpa<==17;X,DGGsYTW*u+ۻ4G EDQFy(][qF-[f:9<(R<&"vpy(Lɀ;uաn:$&${=%=4.Θ]d1""6ptreؕ(L(L(L(L(L(L(L(L(L]HzGrA{!"""""""0QD8.^h2DDҨK+jڻyiDDDDDDDDDDDw5_dH"[(jUj]TkTFQVU(-ڪhԬ]"v Ȑ?Ҧ4Aǃ}羮#""""""""d䊒"""""""""+J&H(("""""""""d䊒"""""""""+J&H(("""""""""d䊒"""""""""+J&H(("""""""""d䊒"""""""""+J&H(("""""""""d䊒"""""""""+J&H(("""""""""W IDATd䊒"""""""""+J&H(("""""""""d䊒"""""""""+J&H(("""""""""d䊒"""""""""+J&HFbbøi&B ; |edÇ;3.;g''{tK&%atrO~888;$_ cE,[{#""""""""'Lt{v`0bm9y;;[?TɯH55vC0r,%EDDDDDDDgdb/'߳ _~3qIܧĐNo.*.…0\gɴj%*.Er\ZpqML<vMc癉DGbE T_lg9~^ {, c˜EK0[,X6nՂ/1bY[/L1p7_& =~]DSS3smZ17ȝήarpqjWz;yB;ɩiڽ\~޼cGSB9GEiGTmDHxB 7Nhdcٲ7lbo<1 9xlB>Exb*J?NDDL~jV(@b|Lo~X>KżY[)+vJ+p!*X8Sm;]g&vs8 &|XVO_ g!, -%4#6['""""""""rG2纕+RJ%ޛ9ŒyfxHHLIWLww9K*ԭ\)ZWH3oz]g&櫴|m͇mpqrb pC3ˬ PͿ ޅ 1|tz?ΝY\ ^'M0[,߻ży.7Z5198ܸ*׿CGcաlmź/?bL,#W DV3)a%|o`mo#_/a إ˩DOZliPDDDDDDDDDng&AD%A1ޥư^NF$s31vD/r# O']:٥3mRPb#;z.WGwi0p6&g^'"wBh\;ܫʧ`Ž|7p27y.sbNɹ]:STޡqSwߺED$lT2QDt%bbw(""9~1HD.%QH/>w!ݖ3Ӷ=m;#*;M∣3:[lp1Ƚݥu"rGCd\;Lg1lypwOSJK`RnWDDDDDDDD`^\oƿ %kDUĝGhV/GQLivyR^7wVr5)9F5Ѵfubힿ2Ϲ:S? W=mpKǓ`K AaSa,a\\")[_DDDDDDDD$5˜GL"*.U*q zld$.\`1S.pbfy-$6*Qڽzr˯a\qv iiC\^EDDDDDDDD]13qɖ|p ƎE횙X[*,ܸ퇎0嵗rlW=- o˶foU3";w0tqBs6n­'*EDDDDDDDD]13ſҭy,DX JɈ Տ%KH%ei n/k0EDDDDDDDDLL3u8mk8"c89 %o?WP }e&Ow2+ -/V&0u.D0{Хq<鿔C\ 1hKc<WDDDDDDDDVDWW"c tܔ4~߷Ed@zl`'sePs(`sy Og/""j|qW0 !S"#l.SޡpvvwH""Gd3UJqznY`z.̢.񮥻Kwe[6,6 F|\<&b59u&Ɂj>@TWG s8r|!w7a<֤mws&s""r= ̙:til6F<ңOLw|v27`lhӂK;4\+~?Q>=ĿO3")Sc.p܍ t SѾ""wC1 D_h»7q$'%?XL_ܕ8&~9ĄD>\_dl6{wDDFBs1"𬘏7 WILHhQFeϿ1ed-b ^ZZ`Zh4o|&;E(";wE2]Y.}2+$Μ͢ߦ]`\bnu7ojfsJ$ؑb%)1rƂ++q%/GS)FV2g ^sYs=Ͼe+%9KbBM]ίQdqjOщf3ɉج6{rW0:qphw'5܌d^"#._ޟFJ-ptse~?wǷ?~c ;/ek09տ&J%|-Ƥ1xyKS/"kt!ظf#1Q1<C{q<_\*c2(S 5N]^q090uʔ+O ^| )5 =JJvܺ2 |N-MiygzLv:?^|rvTDd"dA9?zI9+NRO,xX@kxL_>aiCZ^{ Cnwv<*޹=oUxVKQԫ[VZ㣢NTTGaӊMs?{uӻC+Plak.r~--hZ1Q1Պ`׊7?⹋1<Տ(2" W?LdkUjj*/^$88 WQ\iuoCC2}%^ώ-;oLS}6 vK_t%$[/D^dmJ&I}04ircuˮ]Hہ͠"+6*3Pܧ8^^^K5h۶-gEp4мu 8?gL-;\_>Ҕe3{lN8ūʖ߷0j(J,cL3TW_ SYh91Q1?}&&*&X~g_ܺM ?β9˩^:=z-tOJ*Eݺux"fȢ1;n/!vN+DGPb[L_?.$hRRR)CMl ?,`tӍڲn:fLI^|E'#Gfrnc8v8uKΙSRR7}.-^3K)Mx~xW҅K|8CJ,Ό3Y:)#.;t7ǏvɁry1˗.FDNq$7}Cѥj0`꫌qqئN6u*T_? <%JRF4tlH=z8YS"Ün+(UC 9t~Lx<55C`㚍,JQD^dEYI'RẙׄeǖҎM`72URN'r4VϠƌb쟱l|41 ;`oqRSR9zo>ewޝ{!J/dY?}2즒K-jMJO`OW={Yƒ/Æʄo&r* |{_WLjC_{Oy҅H?5(YtΜ:cgͯd&}}8w`Nq5.(3(\0T@8]f޷\qh Q#F`)Sk׮6m1H}AHMJJ$!H}`NѷG1Cx ;uO_)/ظkvQ3Wڿvc)S wszt|#kqWmjRz3RS΅Aٵ76/F29~8<2>N l_s/ Ǜm[I"vPHQuv%򓳳3 R%w.=W&ӮA{>smDŽXd>%|4{"Ά~}y#$%&ssccL=R|7gԣsNY617LZZUB@ cKϼDPjׯ͊+ظ#[oe9X'>3m.n:uxwH.鋦8th ȇ\]dbApp8>,xLaɓ1hCsr2l6 EZihk>bw8ʫsͦq ΅~hА'؏Dz=9~ 9q43SNaKVї3mn-ZNXW}Y?ڌ"E{2 bl׭[N;e[BNJL} 5diyf !e:r?,I~=hԼa6bM\t{tɬ*"7±o^򁷷7 4$hW߇B=,Zmh$"<92hT|ɟw`f~<9hѦEfDL9<ոlxⷭyh޺9^żܣ 'Nr,$uVK7/[A+njEӳ,XgRH!Ƕ?es٢e |q sϡW^4n>FEF1ŷloTZj])XPJ`re8p۔)JecƎ,L^ȯ)dK1DїIOvUV-Rӈ8wޡ20un\ʐaP-LX,V:~'ӹYvoߝ?/f֔T>[&GC^ _Wg;Ȩu9y@֨fcfhDDzw^\\i۩me?/9&ؓ˪lkԮݎs`0xOy"3g1LTVޡܳj׮MdD$I %K0qַ,ߺÇR8::bZ 9)sudˆ-Y]d&-Pm4y!@,>3)nYΜىIl۸ #vqt=,@1~ ַPw:t(z0Nᩧv< wִ4Ć \˕+MBPBx .&rG**SZe cFڶ+shBNlA|4@ +W1qF^@0)\pZj]t]8vks2o\B)T8cC$Bf8t˗/+2+8۹G;e#.}67O 9o/"9çfRJaو/ XCIMMH9|saxϋ|1$^Ĥk|2YoUV9^ץgky3oٰlj%ى!׌v-sU:--)_OaTUI'^."r'Q2z î]ضmط/X{ګU0Ny6GSc vۑݝ俿 o\\]נcܺC 6&OG|Jk0ɹmmcSu,ReJ^cOtd'c LDxDf8+&:6snGKsR}_Ţ+""%9w7w{qOsuuh4޷rvvN`a+ؿ{? ~ȋz3(NNNDGE=s xbd7mkèw>tiN9A _}_%==2s~ar,s{ޫz<^=PȝLYƙ3ԫP*]o:2A!%Eǹ6or'^v2'F^KA x7yT)*T-R6-XczwמŰ&-i&._GϮmI+d]/gKzvbe,JoVͥ ~d^9Xxĕ+>p:upרyC4mp1Ke}pVwwЊ`5Ty ))l}l5zl}aG_aXiд&""w2$llb2Oxzb'lgbWW&`{衬˞{M2>ܼT2RRRHO  IDATO|b+& www@+bOf9s&Ceٴ='jVs\lFef~;tU+~7x.lZ53l\sB$\MeoTcqppq}6.F\>{1dՕ>xjggܕ{_reسc/op{_]N&/m$%&ѥG, DgggJ,Υ 4jшG=rӱL׾͘8q~ozgu+%_#T:?I9;טah_SЧϿ˞~'O|ZziִXVέpF_ jҥKNdd$DEE+ɔߑf#%%H>Z,& ooo)VJ???\p9d(_<^ޞDDzxb`uۇeˢn+gώk/ ĨGY̋)'T4H":LgWYFjmbL&Ξ2KVвmKf-nZ,"r(냟iϞg7L&=R/Ul0K¾arL a#qus㇥310+Zn0|9s G*eoWgk&]ً̢xü빊 6GGGW.ħe|ѿ=/'#.P`} }0.Zz< bK#hgKMMɓr Μ9CZZNNNRxqV^^^xxx[fpU#66h"##9x Wdn(^8*UJ*T\bŊK9ѷœ)[ޟ}(&*o@)Y:c)cO>&p`^d-e1[D<\n899,q!K-fT 5Wѷmbw?вmK֨ňLv:1Q1HɦXU*X""{Řǰt/T\C싏QPU+| a7ʠ,YV&~5ؓT"2u։iL%+شn|s|PֳoO~[?,Mϖ#1ѱL?zjeJ$].Ħ͚1q9BN`Xxul2:pqXܬց6hqd _ \h܆fNp1dJƕ+W8x AAA?~LRP 4ϏbŊrͪIII?grI.\HJJ ԪU|+jɅ-`vnŁ=͏Yy )LLT vckǣ0E ok3Qܺ6[Ű /xbQԳ(^|K1vم?.dw?`N7ӹ{5m;eok_ӝƁM e9D8歛R,O˰-*""r;nΟwGcXhm;ÿ?^E=X*bcqqud~tqq/A3aOS/˂ٱww^|[KLffÜngr]'zwc 6'~̛̩>jԮAj섫 l'φ3ӯ۾m6TIDD LGztkЮ~]{2q*_|8ԬgO{cX߃{WS"1不svѣGquujժՋUm+Wrʴj Jxx8СC]///6lH (^][83mT~2:ckɸPBO>1߳+Ǹi(Q}֬S +p*FǞ|m?4B,] a9Y7n٘ÐkZ,BE ѶS|GDDnɁ'2a7̛1GC8q4$Ƕ|~eDȨьz3l– [/Qc&Er{JUA /ř?,` Y,=\3u吣!x ۗXNDl6LJJ#8ujQwcNE_P?00:OI\Lj˞kȻc_>JK[ ^ƍټy3iiiԬYz|Wٻw/{!&&իӦMNߌs1'pW++1= lї_}IyC@\?($*4{պ1ѱlX;Ǐ . >Ԯ_G=SB TzYGp.nn4} ۟4Wf28; guo\6lY>oGÇӡg{*TpљK"IINWQ*WLVMp뮀g 9x+1xz{`ݚ<ڱ5..Yw?|ag/-9GC8z '''ZmygNa?;FJr %JqF6mFbb"uסoև}.\7kϿt祫)1>'eE`d?^͛ ;u{s([ׄ_'L0hЍ/ TK[ ¹&Mf%$$rJn݊;͛7k l6BCC?8ve˖k׮TZ&%][2Q5J&JQ2u'd*T=v%Vе+uf,{<֭ AlL11;~Xw+իquu[nԭ[u Cr/n:ƏO5޽;%Jw"""""""RllY7c}Ml~e?e8;씌#wΏ?HTT[I&8ޥKח}ҬY3-['|?NVTEDDDDDD>d9s˖X ?+ېZ,bccl6bb舋 .\ooosne~'*UĀ(ZCwʕ_f˖-,[`{>͎;w1P֧:l{3={xx9s0Ξ=˗ q #j CLZZZ><<<ϏrQlYpppȗj姟~b˖-tԉ͛W ͛7Zj̘1ѣG /PdI{&"""""""LĻ]ٲnؾeϵ_!478vKߟʵ*\&gWg6[3LKrU\BLL QQQ? pvvf͚ԨQ"Evf1k,8A ɝx 6Yf1vX~˔LL?[FXڠm-{ e۷t*T@ӦM јٶ>ܐsggg)VXsr~g̙CժUiܸ1k׾駟ؿ?BUsuue̘1#FiDDDDDDD$(x1tX{Xe 3 {Y,vڵkt˗k׮<_"@C;v'ɖ7TӓS~}, ޽Yf1|7oNV(TM˦MHGGG ĉ1cZ;LgT{Sl|%8Ejαba۶m]WҠAM fp dvͦMظq#M6]vvK*3w\Zju_/m'''øqX~=m۶wHӧs̙̿<2edi_|СCu.]lG7u]rr2}C /hнdbb6hm„lN8~ʒ%KU]vD*Wo:WWW7oȑ#yٿ?>V5˭5kNv |컅m۶e͚5$%%;RZZƍ#"""۹Dƍ˗Yv-VRSS7n.\ȇDDDOx3tug ٙ gԩI-llC9U=6L4hЀz?K}vmU~IJJb˖-t:m7?`˖-Jg}ȘA;qD;G#""""""2Lו)q& |m2Rkɓ :>}["" p֣՗du֌1>s6n܈fˣhСC jA{#ueaX'$$|v?~,Ϟ= FƩS8}u?EFFrqs~}FRL ]''*UC{<ؓ'MOdРA+W}|<)Вٟ@yW8s cǎ%>>>Yftؑ+2x`֬YswAx(ϮL?XVF;3WD$W?> .lٲiӆ1cd{E Q*rx{?~ѣjD|;=f1w\9!C(UT6Iߵ kf> Ǟ=1sꇧ##܏3 Ņ &y+۞=rmQQQ <==~_'O2l0;Fݺu|oܹs̞=___믿nȥx#M0sFtaZǎٽ{7wެ]ڵk???͛Gxx8cǎfѮ];NzScyzz^"""d=kƍڵg}2e\LJ_XvmƲ; ,t6u~\\\2df3fijժBtʗ/C̙>>>tޝ  23{ɉM;8; իW EHR22 ?{΅c[Xb{w%%_&9=*V5n{&{bBq\yv9Yb; rt,f q$\dvoM>)rJHH,UlY˚5kHMM{;m۶e;[qyټy3)))Ν;DDDD?ӍH~'44sΑhÃeRre\\\nﰰ0.]J.]/{5mڵX~ `ch|׬^:d9oZʕ+KRR? Ʉ;nnnl68@``m_DFFr̘ҰX,899䄛Ŋxx{{q䅴4/ZnmPx7nd 0ƍƮ]8um۶ %%-[RF x J,Idd$3f̠SN4L4nܘO?.]#p;A_Hl|ƅz/lOs*fNy.u 8 gi0աPBYV2je9L0+n?ar4Ѯs;l$<~܇굪hO3&O?M62f-0 [-Zd-wȋ>fZճ[bo<&t3E=ҴUzh1ˬɳطs3c09+,+}o)v6w˲Eq09вmKʔBEŠ%+8v VϖX2)Q]UK5tPׯ… qrrzlݺ53AAA̜9Yf?ӧOpiӦYgR{yy1`oEa;yQZ5V^̈́ ).""r7y믿^:J*پħr!mL˖-ڵkjvײrJҮh[b{_>~9#K&Y1 UԬ(?T*jKhUkFR];H"=pK1ɉGN9WZ_#M`3=.N j'Z!{vgΜA0o<ʕ+G@@5jԠEݙSRRYvwݏx9tJTTTA\b2~:͚5k%22zʦMORRRX~=:tTZ5>{qrrbРA 4eɷ]wqqaʔ)]۷}+l|4#LFAU*lg Ko}`Yg/qvu`疝=pǏg cnƵ+?K?]и%r*ycʜԉSXX}3|7ٿ { <굪ֿOɎ"!sDFFsLٲe9r=kl+_<&L(t q, IDAT?}F+B!bpE;>|8+WX{{{"""ȑ#,]3g"%%͛7ӽ{GK( #0EF5{X˞o Piד3#dbZZ=zK.F͚5J*[$ooowܹsZ*5jԸ{cccٴigΜbŊk׎pʔyBZ/// ))m6֭[GÆ iѢŞc޼yTXVZBz FQB RS;@zzDvGϛ>,gn&p$-\wc9&i_{P"jHU>vt/a m_DP 歜{]B!Bۓd={)SBϞ=hEݺu\23f/`ȑxzzuʕ+  ""6ma^t,{޽fٳEJ_ŀvquu^ztڕ>[JݺupsaРAEGy5-[ɓ' 7ߤRJ|tؑÇqFvEƍi߾}1 ̚5,^u4Řpػc=T{غa+]{uksN|D23N:"1ɱ#INJɑV u"PJ%Ǟg<ծC"D!B!J0I&ZQbb"SNv㡒, 6I&1j(\\\nwU:D-LS==1,[vTtg1ΜgBtWa͚5SZ5zMXXX%awܡh6ټy37n|1-AR~}Éf՜8qݻ[FNNsˌ9ݳB;?msggX(;SNv{)r|j5xخB!hLŋG޽-qttW_oaӦMۙfO6mR##1WΝgF-懔ͯ޽{ eIXs, mmYhԩ,[ӧOӽ{w>}:Ό1ovvrrgϞdΟ?O~,R*۶mcڵTZ>B( =^{w>+W*h%F=HήlսZtV]IU!B!D D+8{,wfРA5^|}}i׮W&""e7K]ֺ\9 K^ܲ׊azOhhhǫjE߅ƍ2*d- 6$(( PF zuݷ-""~G; Hϟ?ʕ+IJJsδhѢXw^ R|"^>^z*Uąs8z( ix{9 UZp][[_Dp`]F;n\IB^nvv_+D!BO:%X/Qzu3ի =z͛LQQh?L&Lo6Yf ӦMf͚5D-U4UGK哙Vl6S~}^}HLJ7xɓ,HJJbΜ9L2www>}Y/D1)SrAmd/defޥ ^I&=o6xmY Y>i;h۠gNy5B!%D ;~8qqqo*k4{9KJJ 'UV-NS#"0كMQiμ|8Z"QU3g?0~x1bCi O"F{셳3/Πd>qۦ|J|  \^x}տzxo$jxmk|2z,*`q#UH!B!CRla7o&,,̪ɖڵk֭[ڵ+4oj݋a޼ey ;~eee1c RSSy7)_#fjgp]~P:ud-ɉ3yd~'xǝW\߿Tj׮͈#AB[\\]~:7O9x$;Smil8uGNDF|0|{vaɼ%?wM_qF;8pdabN~cP" q&f2zw^nJ_nd] !;;| !B!ɻx JII4nت(BFسg.]"?????YD@= ݻpS1m4-H8m:ff!AMxh42w\ѣcSe%f .fK>3YĩL3|CjՔ\sw!#Ƙw1 BBܥBZI3w,_V.^EbB"aa|u"X;$B!(3т8UTZgժUw^=A7t(کSٳQHtFfϞMff&oŞK6ca#ggXd^5k֐ȑ#F˗K.,ZǏLnn.~~~ԩS>}$Hx\u|t~:v7pp(|C:z>IĞ%''/Ojռ9;8y$)){S3fA=gvܶ0,<,Nc;C@N;ɕ+t:T%mqnuG!BʂpG!,,X0TZӧOJLfsy篲6mz˹tÇxR4DBEh˕9'$$}vzUQqӠAN<ɟI \rK'vvvS>GȺw|͵ox':P'B!$h!YYYVmr'իWgÆ PׯիWIMM%55 =zGGG<<}wi}FV~>.]\~sy|||?>yyyh4z=t:\r-["PNTUeҥTVz]veܸqdffJ"Q!B!EV%00֬Z*+V(Lr gΜ̙3;wt:ʕ|||ptt,([3䐓իW9{,׮]h4H*U !$$//s^=VB3t(Sry 5k5\\\ iԨzڛ;o%;q (x{{SBVJ͚5qqΘ1NOPG[K:DBBG~JڴiÚ5khܸ1eʔuHB!B!($h!qqq<ЎGU|yt:yyyl۶3DEEoA~L&._LBBgϞe,]"""k/11sIRRRp}4Y~iоC=l∏'..˗h"U׿?Lٴ?LVͽKUUeӦM4hOOχiҤ [ne˖-'B!B!xrH2B-5^=lڴzѹsgFV?4hUUСCl޼5kP~}7oyyy߿}ԬYE4p >}pfDթ^fWSğɓ'ٷo֭Zj4hЀu4"t8hlLN&;v @1d:͛qFZn}ϒNooGnn(rs+_!yyTd2a ?B^LWRfbY+--kײ~|}}ԩu}݇BQ cǎ=z-[_իW[nTP9Ժu1ލnݞ0w.jJVZjTV;rY… Yv--[$22C5D۳gu)u 6lؐs4ibplՙ<233qvvu8R^^W!lՙׯj4 |zO*?B^LTw"'UUٶm͍֭~QV-y'Zp9q˗/h4ҺukZju73ٳѶn77lqT̝:=r\Lpp0\ 6СCR4bbbJծ[ c߾}Ot2';{;N8A lr<={P1wb8̙3%["O +yf.^HGe'N[Y7["BX$-$++˪ ,_΂ 瞣iӦV݉xBCCQ;v`ڵ>}W^ys M={ }]|7DEEѪU+6m… ٳg]t|5GKK-M->L2e H|%Mdd$SN%55[cZ6oLdd=K-dB9qͮ]hEٵ %)pԠ Q TUe㦍TQ;{;B`<\  `ƍ 8:yyyl۾ڑ~!8çd2a6P"bcc8q"#GҼyHEЬY3Fl櫯ܹsEg^֭uC;u*v-[Ec+S QQQ5 ^τ XnyAAֲаCQfI0@ʕqttɓŦjIZzW:;w э_ %Qc/ ͉VR6l@d|֡!qF>}ݻw:REUU-ZQonpB*bf4h:x ӦM#44Çe5,ˋaÆQvm{8P=3Y0Nr4F,]jؼSEγ }8`Ͷ999B<9$h!dffZd͛GƍD--[$--~~orv8cjfF@߸ź==cɒ%̜9|]S)))w=K"AM DS:*ONN&00؞3ϾЂfre221 RrxMsГz*882|ql@͌~왲[۰g<"ppuw'*^ei9򹜐LVF&&5+f>Jk DZDgNN/*3xDvvIo__bBLAZZ#c6?>DEEY 2zILL歷޺s%M{6hpW/L=z`<,Xjh޽;̜9S ]Frnmfq?ʴѷUyw^booϕ+W$7ZJn(Z-&W%5BT(bނ t{jՇ_‰?2?ݍ5+ =ÀSi: E> !ގUsQ'qT=J;v ^g)Z6ua*gsd*7*:F!FX'W\yyoNrr2/rwleܸq,Ybsj4zIJJg&vLC]VPbc--QQQԮ]3f0''2ev) ^䜹%(8::mPJ5˨Хu\\9K?F+yv;3;:а\MIfӁ y?cǏ*1o;9cB!vf3i*k*k b/v-(bt/Bue#B{em~%q8-ޖ@aT$B!$-bŊ\|1۶mΎ&MN&XC&Mhٲ% Xd4il߾1aN Bk jݛ .]Z|z]׹ ,X,%h,_{r$#qKga,x<#1jG݃ck;{t~*QY"3't?t] cv˼6!(]yGI6|Lgmuضe2B!$h!UVEUU.\PfvE-&___VZW_}e9_L5k֌]va6j.sjoZPoIJ^&&&^h4k׮zΟ?-_q0 YbϿm\9iQn ީQJDQ39LȚ]ѻ-+d#D!MQo=J!m;H2Q!['''*VȩS ygϞ%++p+Dww[nEUB\]]mcǰ#$$V^MNNaaa4moɐ'N8x +V$&&yQ^=n[c߾Ke=<=e v-Z嗬tv&>>OOO:t耻{A̯JNNuԡI&bN:|gԪU gggnۭw]FZZ;wqNGfLQCSguRFŒ&77[QP IDATj8FƯߛhs42۷|9UTvrD_|1a WثŨYe=룮b swe9^+oS w!u ? hҥXjUiŪ_\PͅժK@8 !"D [.[n%**$UQ?NʕqssRtw /ܶS022[6O?^zNjj* <'ܹ3 _9Ͽ+۷/%))޽{Mhh(:?Fh"\\\ l$fE9sp:UQARz:!!!DFF0vXܹ3 ã !C0a„B޽O>Wr1f3gϞM6k,ٍfb֬Y_q{REBMT+qҹ-XO0yz',ƐGi iJDu"9B$-Ν=Me?Ãz0wmE!Zm}!%OǿS*G3\=j[{E)qB!Kj,(224bp\\AAAV222*iӦqFƎ˖-[8}4/^]v̘1qgΜ!++ ᅮŒ%KO %F#]t!..]vgvݻ?3fcYv8ծ\PƼܰaƍc֭1mۖӧJx?Ν;̙3y牎fС_PpdeeLbdzgg׏H 77OOϢKK(._e;T˖:ߍFd1cqK6lP+ЭMV.vdΡB!J<;03Q/$7̻C|_.d^z^幨bC!xRI2т<==Y&;w|TU%)) +Ef9zuQ䈃Cd2q>/  ))n+VpQLBڵ ׬Y7xEݱ)Ȓ%K:s&6`X%9r:`7mZq[5jh4rԩq_5FӾ}{9x :ubŊ̝;YSN)#%%G*ъ%T(xKukY;ƧyF+)z2[nh=&!c:S)>P> UeB%(kBUlr2HbB!5Ilْ)Spe|}}\ C9%m˗իuh$''2en!sN4 GĉƒCRR*U*XTT?<1ԭWэr0on;fڸq]6^&%>$\]]qrru(% ys\9k y*rs2Tzo+\3͝c=uYA7oBXu e:*/˚wJ$ޢ@ki"B I&ZXhh(AAA]~=Yu lr'cvvvA2133FxO- WՊ_{,_sef7..z=j*=ĘcC3m36>9wժUi +k4Zy\٩DuK'[:92;g74ɡ׀TβFU!D19Y>T%ʻŲB!(^L]2~xN8Ahhh$ ?掤[sTFӧ|1Y3t39*weƁ믿'ФIN>+D[][5 U}OV"&&#Gsd21qDy6l.Rw/(hgРAdeeh"{:Zߒeo'BA]-2?ٳ+W2n8Vsl>u(Jҷۼk~)i,S4KHӲb srOoѳ )JGg1D["Jq;zP˼ty >H[$뜈M&mVVre}]?~CTԩS?0 cnGrqo>ʖ-K``0ŋ!#m~=oqrr"''FC-سgiiiVYq&JE_TTƍj* hX*m_do]TkWЩEEzu `jgL&)B2Kfu3C!4RYu-I$ !iYL߿?-Z`̝;yF b0ؾ};-[ѱt5"e׮]kE}"sE߷/Q؁q4Twto عs'+WO>vdggfڶm{ײ'&3CGa+QHXBr7%_ez(ٳч|O \ FIgg-[/2oeMZdl+ d@n(BGghu4WkLPS%D!#&22Zj`l#cܹnpܼ9;Qz eT1/RL͛ Jvvs|z*-M6Z1Ǔ4Zy<7a$kCS9~Ԏa}Ҫ"K湑+?ބa}~nu# WMB!I>m@}`00|%,AUUϟ`gϞH_ekעoؐԵkQkggG߾}cٲe6Mq]7z{{1cDEEY9nj4Zy,Ud`Wtc>BfuD z$G !ăثe:f4UU#D!%Ņ!CpY> EUUYhgϞ_~8;;:,{W0׏'O`WL6 lȲ{n֭[gh;Q!CjCkoPۙdD#n,ޞK<g3`ݾ|77P3S12N 9CB!MlP*T`L2UU޽c1L,^G2p@m37ka^۵ŎkaH&))˗/c0pvvbŊMsJR3Ds"Ί3+999̘17n0jԨ'[ .&{gȡ\{ghy,q{~3a2 3_!501R}L7ܬ$Bǟ$Kjժo0uTMF߾})SúlfϞMbb"J*庺2ysuuD;Fx6mJvEQ8pg&##.]vӯȢNvl2R\9V8FƯߛhs49^+Ƨk,+?to;WC> !+?CB1HB!J)s.Vʘ1cb„ ::{,&L ==zN$mXh剥׫}!kY!3X܅f ex BGOԭ[=z-[ҸqcmS^^wfƍ8::ҿBCCmsYFE//4:y2wT> P6-ѣY|9s%88Ν;kr,_(K:Mf凎yV2ڑ4ST˸)Uwn G =ݠ+8:mBXUy0o(ӑDBQH21F5زe ֭c˖-4jԈƍf)--]vk.f3[iӦbY8lڴjժQr( ʛo[sϞ}|^[n4lؐ _P^=Znb~ŋ4hЀ^z鞍V x(QJ9"Vĝx6 Ie2pr=>S`uBa8I>S>*U->$BI!^O۶mi֬;v`l޼`"""Qdeeq),Y` URB*hRUUr .]">>XԪUb`4Yl 4 88ރ==ѬYWs:hGiݺ`(S^=N8]X`:jժQZ5T7:]srrHLL̙3rE\\\g羞-]6o-7PYWW}ܸ-XO0yz'9Qܓ@grhLԳ`;KmcjΣ{4:vK^WBQ?Oke$BP$Xi4󻿄L45kPreaɐ̂ hٲ%{I<=Ѭ^}y(Lu뢙;m{兗jzu$֡<i"Emh&Y<׍U]YڙzO{ Z=V'%"u1+UJku7<I$ !O'gۙ/пTU$//!Yݵk sΏ6٭n;wB2{1`zPy,]9?px7Tx'D.k#J"QXEy|4>3\coo=HqcVΨgR0x[yˢsK"Q!xH2QŅ~ f͚U7n`ڴixzzC{sa.]Ps˖hbOWةv[KZغ ]^G܊c(L=lr2Nf&UD%>zכs1vQ!P3xC}{J3=;[B!ēG<<H"_*_JDBdd+ƌdbҤI$'':$aʔ)TP#FXl[eϻv 1& Yt?mmТ7(L2tV,? r8=/TNcX?:4ļȏ^!D&u4QXl^I$ !gTT׀gf轊(RTAP,Xbk%5%hbK41 jD{ލ {~8R9X=w=goK|^ܜI&QB-Zٳg;7T*ٽ{7K.qƌ5 ]+iHwoumɓR?niTDC%Eu"c##v(.] :6 o;Opf.F*U1o5:2w5Ϟh \ej1xW$AM$3n8:vHpp0k֬y'=?t IDATSN1tPJWiPeP=Sv/Ai46fldY2(E]bh$`4d*3|=5geAxgœLN:Y D  bZP(;RfM;w.:tye+,ǩZ*ӧOʪJGڨ>}P~1c.\%W J ߃}ؙQБM !LʜmkD}D!W Zf7ez4ĭN&$ѹG Zڢ!.&yFW,R[Ĥ\G %JctXȸ"( D%ᅢC<==ߞ8Q*(ҍɩ*8N<ɓt04-c}K}V{i:Tf`?R5 013^zTX.{C$Ib$C)%IƶM,1'V L⣏Ň"r2,_R9&&33+'C]R[Py OْIcffV* #Μ9Cbbl8!*:G܏q8ugr/015y1E"QR247\ޡDٵkgϞ ׯv9ϰKOOٳ=z|||EOO\zڵ(Gk ܹCzhLGrcڵ1?YX #U nOl ɓdFZcNξr F\6D2!ϑpx!kq>::*:tKaȨDj*z+J³[|7*T(D_Y;tsয়~vݻw2e,&&rXmmYyUhW 3a x9IJ2MY.eđqvf&HH& P"(88S֦~xxx`oo_f1(JݻGhh(ׯ_GOO֭[SzKe~~zS6ˆ Ç ǂiذm";ٛh EWH\ߏlꗸ[KE2t0e6c9{d0`X"m;!6E-CyRbcSg#)pΜ9̙|yO,;"R&S?f6:-.Y}g2q>f-^mf俜H\3sHH& P"(tΟ?ϩS WWWjժswMNIIݻܺu;wNZh֬CK+ rT6 iXj P/ɛU`o_De:Kӏc.5^mH͋u\թ({.l QX$11.Xr9&[:]D2\ƦuflXmJB*9߀dMŇy?}{=%,J؅ Z%K;^LL ub۶9KY'f?trCܿðOwڕw-# H& ٳg\~ׯs}T*666QJ,--ܼ$czz:$$$Cdd$=">>\\\pwws%&kQ~)hk#]IV-B:Գܔ?`m$2>5hY'QFHBRِob[9 cK8%I$%f￙s/ t 8U.ՆhB׮u;Rյ/:Iн͚텼9$ˤW~2wE?vTx"(M"(BacSw-oߞ >|Hxx8?~J%zzzd2ARCFFrle---,--M68::P5Kd@dC٧'} Q}ܴ*QUfM*hDQߏҭB7/__ԏ#E_2+hBzS>.YljCVVV/3< &S63aJtˢ$NAOOYa4Z%րm镹{7ʕ߬3W1b=˖U3m#{a^յYa6cꓓmD"QAdP.d2f FFHU(GBѠˑS|r"B '_n%h0, HNLƺL+EU1c>.MXܜ*< zKh"6G./!IP(DB4%&&bfVe䉉l|wV͚bclˤdrN,_~B؇6Ps[[[ؽ:|'}n&Mj߷p"=㗒f!1\H$ PofU&8HQr@x]2q(e2SPx͇x!#PmaddD>׵Oê_pd0[\50T*U?7y ss7ܻf˖s݋С4_Y$ gڴmdg+ظ%!!l9/?GbP|$b KL?vD  'f& »V-Ρ7Պ(oT/{^#+ Rc:q$I׋HPV}$Bij_7tٸƔi[L?I⃞)ȴD\JBV ڌYj;6ɓeH֯ڵeO&Lhx* --)'NLD"vʠA^<{JΌ 3m6>;КN1ܡoVOS$AA( 13QH/Gf ;wP4hjH6/HWAvaH$ B ph1m m:BKd Nz{ѸWСۚʕ8{a;GSDjլy$ u3:==m?ON"_)oV[X$AA(*13Q1yxe~HAt)whwÝ0*F+_`a`g ȟ YԜZgs|0.0L&eŊykXBttd*DG0b\KVtXF^&\!  H& »ih(ɓݞ/]Bͭ#+X!D*ⱗYC~V%.ܼ+Lٽ՘&nVeJhBdr ^OO7E21ѣbEΜ c-+l>fMb)  X,*==#]UDJ//TkcE2BYJ2"[LC2jbn)4~ECPrfuF Ɨ~g3R;P~$jW "´ \mFH R=>ꗌnhM %E-::O23s !!]s,WEǏť"/|qSzjmW7oi=%H$+^m-- 1E>CC|T@!k+fHyՅb\+ۼّ~12~`H$ Pl"(IϞQ6 H,ҙJa$,02 jc</%ox8vqq_k֜l|ÉM%8gƻ~ m,ʕ?~ ~u;AHRfpL={ϕ4ݻaaDE%K` _ٯȉDy+NGwstER˧!1_goB36Q0`X"=׵OQ+Ua\W!Q豔ore:AA0|DDgPwT~ #:z!z5`meWhCl[w)/'ڏv1  E!Uw{޲?GjqDP< m$[Bzldɒ 8v6 WԬYMi]ĉ{0ݍAUwVgRl*6ˢO>I,xޥ"5M~BJU4VnzLHMCuBviBn;w/[UO}{Wׯ¾}7<^;LT*_ܻ\,ɰxe}Vb.1 cqDe5AA "e$usBjjHD9e+apE7/.^H,, QuXÇȵDг釳Ġ9zgc`Cbbׯ?޽h$ zzڤe|4\7*/|h>m<[{XU6c[Bd;ԃ{cyatj_IJwus BCyR$.P()(%*W!Ty HA_Q_|Q;\NɌRU ݁l;0f..6(o>cGGG \[[SbbRU"11 133 ##L*W.DX xhT**:-CNl̜YvKӰwD"l<1?)gP8ٱnr9V0 Od6cl+.sOs% T?_JRB?-sR/&sVVDAAJH& ˞U>>( At<$Ţ+܎BȾ矷)DXZo<ٹΩffx==mT1 +6:78VtdO&1s>^xxKͧe1eVZAn%se2%̔5fR"( BYb=zs64mOOMjj*昛l1?rlTf~iPz =jI7c4xp?'GPG,a`k*U̙<m#*VTP6}ݻ!.D0غ)ka@o+D¬#Y2a3;F-O'_I!V}짒ΤU;\A(?Г[aUflݔ> @}2K&0Or&by/::Z2o^Wm˖`ƪ5n…Ҳffͮ]R&NlVtLᬿɤǾ ~ͳMŊ&ܺ5l9+~HKsPYee)\t: aO`} c?332o„&0~7Ջf杴lg~?U+^gW9sq|;jT{vѱ6061"%Y^l?V7ЙwgȨ ;XqN3tNPNIJWe`hφxeel@7n|7QG1zrxfMLKƽ;Aa"XD3f`TR]bkkKJJ wa޽\~6mڰgZn]wZ#FP(ƆBCC 2֫ IDAT e߿b,}HtQ/{O1WY>K^j"@ խ^ݺtthcyuQ?Wmy~fnHfoD~$wyl AxH2<|Mټބc q'ItX>/͸qAsgwVWz둫Su<:vyO?-dbjJ*itxv/_asrlCC:@Mq""b׸V8u4;w2Lz")) &;+RA*p9=̒ѲMKO<|Lrr2^-Pű 낱$ϟȏF*f.>v&-uN:ùuVZjŊ+^=o˗3m4rL@@@/ 6$>>,XoR LƤI;wnc0vX>} *l2w﮹СCt֍۳w^&L?BO? X.]ȑ#9|5i҄KRn<'%%WL&CTvv;,W/=Ϙj|zn '%|玻2Âw(#1ŧ_qALY3=34͗D2Qxdϙ˦uflXmaTq̡w$$clZzKUr")b S&X1$E#_~! $j3F<; pk8*9jЬСի;Vdd䐐СTBև`naNsuv1{lBևJ&>Ya3ˀ\c%0sL8ڳn:,,1c}kY-+?IJgWsSfM$9rfM!_m}i'[ (`f֒vwρ]L426g3m̞2^ۨ.M{ޠNz))1YgT*MP(c+Ɯ~6S3h:߽uA=?`Þ@4@l9BGȇ 6$h =-hfҧ_ph!Lፉdbt҅+WG-;w.۷uJ*;ѣGر#ѫW/lll'00GҲeK._foDB^hذ!)))q=|||8vyKN6Ӷm[Zl\.g\xyѨQ#>C{wިT*5j/< >}J~ B~GlmmXuN>/XZZҧO '((gҢE :kcӦM899͍77q+^%ڜ:u'IEVLmۮR|gIk=#E"vDCJq|e_4=/-o#z2jΛ81vj{E!W٤њD"S5Gȏsksߟ1uM"ƚG1ycLINe϶4lkSfMGش&(W2ѽ&g3oځJB"аIC:h6oSɆki+,.x6$$p ܩP>ƶǍ "Y?glbǢT*Z?~^ ",7BH0659_rYR ՋWٯ&$_ӡIGlʕLح&Pbjծœ'Ez~gOٓgL5EHZzUEeݾ뷹|2SgO$>ns_7Ȑkͯ&o#ŎIL,]2|p/_͛7ڵ+FFFдiS6mJ&M4'IɠA̤SNlڴ CfEM6&M3̙ꓥcǎ1k,tuuٹs'ڵ3}tʆ 4h7o̳yÆ 秹믿SN߿uJ&Ξ=JŠA5֤I]6zݻ9r]2|\\\xLIMMqݻK̜9;reu+_tttݻkopww/0:IӦdfYHD쒍nH )6S+7O@lrtr|/_rXxL7d.J3$A#Eaf$*sC g-|u4 ê2kjhU(׫Cyo~_=e^ 1u#b˴utAT!IhA{J%NrZޕKefn3tsܾq#ffMEhLS:n4ǽ;uwR qӼ?ߦ^4VB[lO>*rcDzyfd2f :8GmjX2SkäU~|<֛%ptȁ߿'sdȐ!;v ֮]O%'==ss7~9KQpHI+%-3-΅YSۨ8}]3GLo(SqBN+% 9ry#X.)S`z,DJ;wM4 Kپ*6]$,*R⊏O<ʒs_89YQsʀ%sMumۮ-/t^m/D"Ӊɓ0~+̍OY u"܀KulrL9'Og۶O]8+Wg[edt᧟KXZ(`#'y,&BjU+&Oe۹v4<<X[mOira?ƍUۧ}֔k3aB[W1 l^mIѢW'Sk *WlZxמdzqh3DB(G$INGG< r7Z1*T;U;~b _}ϋks-OudwݤW4_~+O2y/vv\A.oXZPQй/?Abb:fueNz^W!'Gٳ9|/RT柿soGq3r'O4) rDttĩhȖטo)T;)ӷo#7v`߾hiX?+=qӮ]-Wqw\1ۄf--~{!dRz~!&0ѷؼ>vmXAlZQG6$pKu`MwUչ*mٽe7}1:s}6W/\Ż72- E:'&$su˕HT\91z6$pu / `悯wĄkqEWW}I&n bۈ3Fl r#>6^$Axc⌵YYY1l0 ‡~Ȏ;Xf sʊdtWJOj? ETcnݺń طoʕ+°aػwo],--/^$>. ojpTRl߰sK jL.c=l\1)?tcc +&J7/C{cSo2/r9kJ &Ɯ9~8qQ#[A}e☮*׬DR컙/^ʻ%ήH$n߸ͪy5'| T[{3ydti")[vw^ZyTmӡgGXD6=5'V=~icjfJns;禾g=vmߴ>QQѬ\rabjªɓ?9rp)c+[؈6]ڰ|r hں)V֖g֟(\j=;2yGAݱ⹋9OZ" ·?IҥK/,b͛Tr#Gk.}6ʕŜS>>-[VS<,cV||m>>ϳ6@ p¬^"Edh~Bn|g=%U@dmLP}O*W΋h֬9==-UyssR,͝@"(0jPKc#/ږ kjڰnZݾϓO04FbEɤROY\Ɔ,\A F@eJmIJaz݃erDGN7-(-ԴuS6Ǎ7y6/VX&rd79Sՠ&+V-bccqɗ;( VhՀ*`mz}}MK}77'L5ƚ|迷V+dlj ݘ$# x}r]q넕zD"ڎWMN;򺩿wed2'͝Db⧀1/{+Ss36mmmLa<}C#CطCY Pr9_@14fjY{L1ܧ7$!!GGUFϣ~ގWnH7 d&%KٳPJ*ٹs' *ϯ,\V-֮]AQsڵk VZ1ח˗Std兟s̡O>_}O* :99%H|W\Ըqc-[ɓ'9wJs Ν; Ç4k֌K{C! x{iev-G"ttfy|}/ysgr2gTT3g'H"/22"|]kLd,A7^#-g^kNnɼ¯g.LI̟EN2d GTe:/֮uL zu+^ cذ$%)xw_Wxw $N=O>S5\?GGy}ϙ-oN)㗒5@*]QD"TO c)kflA\wmMOnTFdװI3( 3\K,_&w_n-QQmwoO b@FNG&&fzfTZ^- !0yd*VH@@˗gܹV^̈́ ҥ &&O6mرc>7fΝ ˗/gҥ[5!Cظq#W#F &$$0qD022ut66_7n @-Ν;4oޜ o(w.S gϞI&lݺU-x)7o@JTRcx;v H1c$TŧXӦǧÆ ,,ltH$:uGUEymKMѣw˗["~[PbHmjD7U\>b'cڡE=)m1F>LOk {U 3S"99t('Od̘6^F@@ׯszݦS{|[אJ?\…3Mk l̚4C(]qqqذgXEYA~?"ʕctڕ˗/SLlmmqvv‚7opUU^z_󵵵Y~=իWڵkɓҥKchhȍ7TtޝZj+Pϧ[n;Sxqd2W^d2/_ᚅ_jڴ)G&44ŋSlYy!XXXPF <Ȃ x=ӦMI$Tƒ(S Eɉ+ѣ$%%!H(X0B8;;=JY1wq F|iР066|Ebb `ĈZ8:Zkug~uKE~֖3)sgnGW=%Ū78D*a3mr= S~ʁTDǠ=2>a;gf&{{3VN|J?M63'?ǧ(z&Hf1bI$ ?u歞Kٻm/-G"֩AHL̠:PD ƍǮ]x/^PShQG֭5gĈڵ'N9880l0vqΝ;ѣ9>Kէ0lv)Hr"^${> B%Kdɒ_u> yXXdrXYYae|^[[4_C. ߏb+)DǏߣ|yg~!7gMRYZm[e(_%5ݿ-;+6 UQ`,ɻP.A&G+R?sم?GnϢE8pqq 0|>:[ZIl>n߮Ϳ6OL\oӵ]RD@\e·Qī-'ED@|f{LdJ&$@xf; ^ɓB9]MbAaaR46:)IBhf!*J΢EyE QA_& MJv9rkמE~U4NNܼÖ-Wx& :Qr _2CHC@WW+gci6a rieO@/}uۍM Fd|;6]K=!a۶90{v~||`„-{ڵO\/W.s?ƍk6Fx1(Hٖ'vve|Y3^ly3? - EƱPHl&"BBTm 6EjAr{%%AhhN20m~ĵ>)R}ٳ2oqVV;7D\]Ǩi+9y)2AAD0Qo-vԮz]$=$$Cxn6%)[tI*S)>ߓ ZY%)d]E.O!^^)W۔'jrx,Mcv60VH R*lb"Ȗ >QvdjiIȖM]"J*IHR>訷zÇӧOR)<~]1`@Uʗw0El;fl;u!,9D`bEPПjc&˝rʕ-;QjSZZnHPHll<~~AmWݻRAA"(OA*bj̻hk>?b$)Г~z7ԎkFadZKg:II"G]kSz~ZqمBrNŋ,[Pm5tt I&@25M&04G.W^Сꖤ|I쯥E*lуQ]jUdu^1eҡ;  bR_=קM*^*;9ɗpqR+]ډ'O{AAAn~w Ҍt2QVOㇸw CgyN9 ohf3LkW,BػNluX|y&p7](| [hƌ }ٲ9QNggg+n~űcwJtHKb&&DEe4oӭe75i&iw3h,W.\֚~zԾQ{(J}z[t sWֆ:6HEL ;DA,=8s\73ʤD$S,[~I{zQF~U{3<֌KyͿj?;](BfkӦ$ߦJYT>}*ӠAaҟ+USV7'p})͝2SGNҡGGM3eέ;X$Ybz}[%.VNJJ10ό) )LḀ+MPso6;C׈F*RZ>vz]˱}N! Ad2)ug ̜yOϥcŚ5(V!K6lvM;Lpp8wᆭn݂L Kή./" LA$8iٱa!O9BqlObbQa1عX9jƷi)A7TNrK=2BA~FK;QAAouu'0p"ٲ}wRNA ʁy6\\ș̬S(ܽuC? bj>>C UbW0vvɎċ/Rx`bc㰳-<,!DGE30Ƶ׈& "~Ǜo@WOGxe+MY$&$r ^zadѓOD">=.)(<@KG?JfX.!\ȓ?O BjD0Q! \by#/dVK Acݘ|%s# {2w/B$]|鿸ObB"bᯯDv 2GG -kM#q9JʕsziwBc(wnQ2ZybʝfEї|؝k-JѦ'޽xqZ$&&ѤjS:r swnQU_(Yu3kL gǫ882e );żs9s lڒǒ}7ޠw^xjѡsNeH!;p BWT1fMJLdhaٺGfhl"߅-QTvtQ|!ChtW\|~?GAA-! M14'wH$*xs**I(Mڳ4ݣޭ$52pio{ZDv dɰaMÆETmׯ?G&h3}xoNDG0j(*V@\̚%k@`j_)Y$a!a?ųаyC,Ɠ)pW_];ܪ֮SG&QGb_3xJW(E}E'N8v޴f3._cPKLLw>HR|.zc1T*C? `|?1ۤ.IIIڼٓ0|FO]ۉ'qqt+}M矽u_t}.{fEyx)`բUjs7GGGST 7LAO||M&ݬj9阫uCl+9Tzt3%y"PjZ&{y6F3\NBBR zzZ4kEsR-:tScm\zkF'kucZbc<V܀ :}˭?60v:_3q5W˞#;sVΦyj=w:r#(NCu^tkٝ[ &ǬسuZ0qRNdfck ٔJ/(6\ιYv*KQFѧC_V.Xv;tm"s5~LpPfhfO zTJMf\ܩw'x"Xv!ffTQ:}6e,ĄDf-s^ghBw4136A~շ ")##| IDAT͛L̲xA7;23#(y)13'E2-/y𖗉o9.5jB!˹,AT{x;i֬y2fCO_^H$oƨ~9< ꫎UVA/1U#DFDb̀=BFh_&&:6}ԪgUk^Zďt+=ݏuQL s3s U2Ks lWOJ)A^j,ZPHʂ$053fMȧ $~GFY|Z&"( BAHO,οXZjEճL"E+gK7z:X@"x_A`^$%")J](jԯǂ~`GJ11K'===H55gNSw1kk#t)uٵz}|꠯Mbi-JҼޗ%9ӯ_&]_r;|ړ[pdQxI?BCBiܢqU0=۞nrJ=u=i6w#дuSZthL&eǖHR,YԱ053J*]my)ʥկ^"j̥y"3QAH& 2iH/nSn8If^YvZ}+nL]hI 갆?Z6AK!M{QOS1]fv>ld)-3JZ7+KG6lB}/&>>޽w1m꬞-YO/jc42^S__Аd}47zww]ٳm s`~σ[a 6/|EO_O-73 3_>\| g.o>&hߜ.ٺn+Oc~f2C#C""=kjnӶ  |NAHSg֬D۶Ųz:;\.UlzZ XJ-ѤoeU_5“EdJم]'iCA AK[w| -.NnDޫ d1`^ O ӱ칃GY!slنM7PX 9}k.Dg,\tS+[n۾j,p"M>~:ϟ>؁q/™ 4lP뗯yHU_1#?}΁PyqpÝN}:Qt]@_3Sj5}n{sƓgb n\Aɲ%77nڱsk6aXxAߗ& .Ӧͥ_n\Q*ciiI?M?ALʵdTth/ۨ(Cj!s w V.."$1׉7I_d`H\X< CۊC3rRG,64r=AU27m6Qbpq2EK+g5$DD$DN]E/M2T*7Ѿ}3Ξ}Le00ͪګWa9B˖~7kž3p,1~'ϱMpp̩ Z߫>'adRI93315Zjٺj|R?I&ש?ƊK ~GvN9sвcK֭XG5(^\BdD$=^tnޅRLq [wۤ.Ny2ڸE#Nm\8O"x/_ǽ;/\V} +NA` Rb%*V@dd$qqq~r>s L& ۯQ/ILP5Y.3{hhoKЍ8AJy8>RcD[[[U/uߕeJs.=x!yfLEUa]sks {yZ@ >޽)ZB3@ZZlC 5amv9~|lҹKRjy4G9U? KM׵OFjطc?ϟ>GOO-ӸE#r9.6n~W7W u.m9s/޽/Jt:UwT#}vN9V6VxXc\AAHHm[4A,} G전SͬJݻ}Vq-{by|%Wao,1WŻ! ^і{0L<۴^վj>Wdժ:W~m ][ɠ3z"o3 3|{V£n̙   EA~9My"w5zeNts@HUv\&Raڌ9AAA` Op=g{T؎h^`ϒSq*(NQ(5ҫԂFHe\ c ~HXg!_AWj5nVO%&&qdaJ)ioη>}eafnJ? ahlM+ 8wptMF[w~: A' >>Op9޾yvvT^ny3qƂ D0Q'1`i+{-[IH$  ^"934D"A[Wl&z}  |'cXDFDb`>\`݊u,eb>M^`@灬ig%TS{eӚMeTS!`ecUϘ0U WcnikLbhlH  ̟:3u4ߵؾa=;3W^ D0Q'abig^k[iG1h>WA2C` kw'@Av:ΐCٰ}$&&qy%卮nee.\'Gϟ>l]ag۳L'uA%W7lGGWSFgx.w3 <` "(q*lSao'\AG:u4QOHҢC\x0reDB}'<4<疮XKg|l[c%˖`т?I&1gTFH^Dx8sČ0|"aa,۸RKmϜiQ%}7[;, AA D2zcz* i"#ikk%{]׶xFEtnޅJE*S\}&Lȇ{ṱr5J1~]Zt%&&VZɈ2Xp5ռT2k`( U;Ѧ'li5OL_@ܻFVz.;6Wg"TmwcSo*Luӡ/.fh4nшsl1޿jd4؈JE*ӶA;mߧ:~z:yu&**tʌ̭l}T*Ug'9v-zj9mZN^>N^ GOՙi㦧xA;G4iiiѩ7%ʔqcU{rɫ3 ))N^Y1GOWg^>iUJMZ =O9m{js. 1TmQQtn֙Vj;|ӥQFTWyZ<~sΑq,ΞZjY&7-S 8~]==}\}vj7jߴMyry.sR UjUUVDGǰ|ӲT Z DEEiFWO7}Q7-c =QDQ@g$EaŪǝ{w³FNbنT]E3qY pvvykZPF QՋW>lPߵW¹S>;媔:5{]vx7n3d?|At ?;+ o_$ ?f8z(>Pf^xŚh݃%jeUvHa}j{SZEnۧ ٭P~zlĘ +pIb?90\UuJaBjD^C{ͻTm xnD#c#BCBٽe7lEp#}wHظ*52ϖ;I&3m4z ֞)2\TB™ [ZϴqӀ#.;!C- n`Gv8wo߼%[^rʉ]l2g/N\岪z_󧨍9dRj¿7Uݿc?ZZZkZ/'(q"3Q'1bmGF5XH6S}J- 04Fv^4ksa̠hP߳T*Gc~?N,QF^ 80'F|ϟ`ףGU+> |Gْ_}1tmѕ}F2~([\kyuAo>ZrU@>P"+7f-L.C*堟y=V*wF;pڍj HɤxKkUv+~)O>\9wOs&a!nM_L.2wX 2Sؽ0W}ON٥*\pBUv9ͫ7jc0Zku2s̓?/zzZNUV.\Y͈x[pB?}ιS֦|rN_9˰aC2%([ wؐֆʲw^z @iއ DAL .ɥf#:K  ,'TYE\|.諲 ssIZߘ026ReF\KNu仙h>DTBmmmrۑ>c8?<,LTr, putY&5+w{.:1S״FF_5G$%%K0Sodž?xӺӧaxhF_Dl_u/ɩ\ GMfi޾Y߫}>F9\-X&A)vڪA6f=(uv+Ξ8 y10#Q6tt? ?JU( @bT[Kjir)Lɳ'&bƊˤ^A2JLAFDFrm9uKO3qH!~u SJ9BCBYxuǯ_%2"raB&jФ#wHux5 &:[vsyQ7Dqr^P&V/J~CR +2 ny(AbB"_5ۡ/P.c.^8j.]TҩL|%˪/1?vPyŒgOgIӓhfnJx!FN&)Is{̛2/rk|*T*ecz*rY.ڠF[[ŋryΟ>Oڟ6 zT܊.H|j_Ao! @[[8)}PY_  aA1f5̞4]wqY1LJJek-AJ*1aW[ Tw7QCrW-ڶ`̚#ק|¸wDZr*V(˫C^c񸻻31׼ǟyDՋfhv33櫱L4INb˃yzyDGz^:>)_^4kVe¥Կ>~~ xs~|T_||kׯMPpIiXq^z|4#5GPpxb1zz;a}<ҹ=_~%Q,y+"wlr[[؅x͡^`7a]mN) UGX}5"v7K]?&#=}{J`p %JpěK/q })Vi[$Nhh1C(##SObX(VޭrT/zC2&rI)V$0%˖Qy' -%Ј0}o5\j/S Rj\#u'++ca2/֍t]T$&}q(ZiӤ-owul-OV]܁42QDiӡ-c5 .rnfU(""7˓Q]nMJncEv!C)r OO(JEnj$_*:v{FFU,G}wn:~V(`  y EDn/o/^}Ȉ=#q_WS't)""""rw , z/o.IDp EDn&ѤJ *&ld?ŇEDDD3x."w(LŚ=Ԍ52~C/c4^FH`Z< x>{K1 EDnP x"""""""!gK4(L(L(L(Lbyr+4dӺ322АG}|S?}4vfҺ>ƾrODWh\Π4>p:];vs%c׎deet)""w$"""""r[dggBNv?r222o>f1Xr,ws{:EJr V2IINΜ:f1$'%n3,S ]IacF]Ǚ %N`$'&F{ש݅Y gqssj*ԨS18,֭\ǡbՊԬSu_JͿc6]Ԕ4}'O`{SQ^Cb2Z 5tjϞgߞ}$@VfVǛs|T"9)ܜ۵}[7nj*ԪW/gҠq<™SgרGa͊5gPR9HFz6mcl^4n^Kuq`%˔t7-5vRjEXb-N9s ]CYr-T\w7tVk6w^ݨ۰.*s<-pQnƱ#Ǩװ&GDD\0QDDDDDnUKWR^Xr,̤_J'8$1_núvSOw>"33/]S?sn8vX,V]'}H`P`uϳ{nF?/~_惛x`#Gg=,ǩ [mf |A-^~'D83]~wnXl"++}\݆u@RSҘLN?EkK}>޴ sC?z]ޅ`d23]h֪qv`>><ʳ؆k6u<&<2/o/f3-^4Wu#pwwd6B0 Ӓ^zZav3Ag.0kڏ7Ab4& O/Oz,qbv@hx( YpGa0 dĘTNuc1mT:>ё*58d>D7d6ų{8jXd;dh4Ɉ/GvLdFp-Fxdy{4YDDDDDn~}-1ah6kϾ{6q:# ܼn3[7w)W9ynwPa^"VشfZ;צck0k6U?z]F&;0_͆` v~SlvlA{yzyJpjʵw~^jF N -ΪHKMskB ]C5_[K/9HlٰO:_}&mUXcFE sI8}6'MgY&{gc2 jX*T@eDZkIdț /{a%pS}_{]$>Ym%y/<빐j ]'"D5^Dˡ}^\ EDn?"""""r[]'`K<50u4F EPt$+ױ%gn Хs v|EDuZ>>.VZ7s_?jԮAZjTQU!h$,"u(\DF޻UhJˁ]xd8kGLt~3ZnnD*A:ϏQ]a2/F݆u-^GyԊFDM ՚Kx,QZpwvlݑgnKǩU&JD,ߖnܻh^?[xkWy'}5 5%ًi=b4M5vT31=/ӏKzo>h^(bwb綝ڞ8v aF.YjՉiމ˯{/m65}Q3q\x)W}~ejM풵А}ryGJ-6qsg̥y.YyɩcjJ yL7=¨!r{튵t~YEF#OqpAoI8{,;]ޤSGП=Bߞ}QknzuM2]kTٵ}?3`綝9x[>/4߂:eɱ0aD5#j胏l\5~WgNnETdmjԊi"rgD-.] xqF^^ӃioSV5֬ ؃Ǿ=\(_} V/Ǝgۦꇯ9Rkh?֎Qdg29|:3J6-fg`.W~ꥫy&Fa}_:p¼<B ezu_~9~8%D$Lthʔ/@JR ǏfYYY<vn-aF$>{'OixwCΝ==yS6#8s:oמ{k`v3~)nw'--'zΛ1!D7&T$'2W.YEn/kC x<~, Ǐwڹ9y3gXxk/vѰi4aa?rm}'PG&R^,+EC$ 3#&є(UǏ'6#v~,u֥s]v͢/ƻC@rR2GzR<8Fҥtwg_~ !A.?[0QDDDDDn+OOO&͚NF<ݒ% RfU23x[2י*RǺ?;F2m40G:ϳiYBZkɈ1#Ǜz&՚sNƴ%33wxant||}hX;Fkwfà Q=|{TZw&^y4oݜ˩X"y:a;=% ;E9N><“>>|0jfN8ŨOFѺC+GE0@vŹ&l6^^{B vxy{߱sNժƛEKL?QDGj7b+C@Gqq,V(xNb% 9qWT}Z3m )w lis{ o۩Z*n|GT:YLtOntFסC$%&];k]Jv$^Hs]AffwYvswEoX\g_/Ǿ )1wpW<\k:qҲ]KGxEL s IDAT⹋ٗ{:ƣ:q vl8n4T\m+T*GD5͌_fC;Xr,졗?HxZwhx0+̩n=xÔ.Wsa4ng;MMr :tLf]{~򒓝:D2TZq,R:Gn, z$ѣw23wy\Wل7tn"""""r[w:f4Z-? 2/(ݩ\-+ױw^ٗk]BcÞ9kCGPS[777V(˙SgUSB<7ШT2S^- ;mjb@Yr38$OO]Ķػ{܈CPryCn 55Ws~J*`\hxs[Պjsy˕ZBJs>v#<ǯHO?iz>գsw_Wky|+vFkz4+;GN=Zm<=ʖ$-5^{Pm=rWj\ fsuՕsf܏<pNǖ-^Fߞ})TzP|ino?w ~ r)Iaej\j.rQD ~Z-*L 0G#9)N:F'^,#nWrs8@k&ވ#,sgܥʔ>pJ+.O8R ~$'%j;e\Xh1W*Y9~Ds~{]k5oӜT&}ugNo'4<k\\WK:Wi63]yr_Uh"5Or .a-^uk/޹;W{ТM[:+P<8sg¹ .^bs )d@\Ƿ]{]:_D++Hdu=?qbE&4ۥDD ("""""˓vDp֭ZK_=pA<ӱ]Zu]evK*R61Pӈf9y΋eܻOthN'0m43xsm^Zjif^yne7旟闞vlP3Atkםnw](Y&[vYBo:\e2zuEf1ihdO )Ru=M\h$UjT@~~2 U*86Zjl2&ԉSa..`솯/bm =kҩ[S=x/3rLZ5#-"3# 582""Ma>ބG;MnA!r-~>j%pO4/yZ4r_~%fAL3i2kڏ,Qym <=='7}!˸0ntu8scǣ"JFGoj*NsnPpzu|>s6H@P Z5?Пp {Yq,?-d2U/^{QAmG mxxy{՞?{?-deP|8a,O~ETpn#B:?]j:*=ɟO&;;_Z=Қ !b8dxP64"5 _Ϛd3qswfwӽgwnU zg |?;,+%J!nWv+\pkV+p<ёZhxs{||ѻؾetMw  &0s,Np; 0Kl22 ` VM.ij4nʴ(RDDbcIlLA"wmGT kPХ+d琕 Kxxx /]5%Ll6vGKvv699vvOOTV+YYxzyTX3'\(oҫ{o6a1ML4Nb LNϲk./J`p ?N{]C~#5u!A2ۙ|W|:jի~/'v~,A 㯨X?xgEDD 9m5e5%[~e {( ;cWppA&4؍,+_L2pG~id˯nX&x{ t{ٽc7Gc%^#K.ݝ=;oWS5uc]5 ^}|3b'Xc3?Vݤ!+wQDDAaVzvvڎkgyrz~uۘn=l2N?əS |<ݙ&5q]דM6w^z  3bp? ZLt nH:ږ*S#p:73(T}fQ  MwODDEӜEDDDD䶪U=kل;.^`-;rL̩l66 لNN?m;IOb&U2%)[ V #r.e+u3;+?~f$^I{17t_DDD&m׍Qb=T\ו_9>0$λτSgȝDk&H\2+8xbbp W*kW8[l51bػk/ʔ˓g/fjw^?xGD֬X4!v~,)_UKV]Zj;@DD?CaaiѦ9&L?8vO޽s7 }aоs;ΘןMvLQ;FvrMnط{o|4Ok.F =s Paj׷o'IKM#/@8!""r4g)pCFRb2oxw7)i,S1:֏s =b4c/))ӗ_2O滯cԙx!zQv .]tӵ#ǎ/0c HINߗGS}=Ķ1m8r0_}<.4oݜM)_cY7ze_ IDgcIlLA"wmGT kPХV OZ5 \roݰvCrir_LfS[7l%++RnܜغmIhشq!;;ujj{80V#9)0>>fʵ$Y:{ͩ9} 7pl#_q]] S(": EIa:+a(LWhDqDqDqDqDqDqDqDqDqDqDqDqDqDqDqDqDqDqDq +2DDDnKgd\,D?@ayRHlt)""ԉK;D0ƒJ/t"/gl.BDDDDDDDDDfDaDaDaDaDaDaDaDaDaDaDaDaDaDaDaDaDaDaDaDaDaDaDaDaDaDaDaDaDaDaDaDaDaDaDaDaDaDaDaDaDaDa\8p1"~^69.馸{`s<(KS-?[c)D`lu/>؟~.1T-YPe2i_+i .DDDDDD'Y`t܊[B1$ϟ"Ö@1S 9y&Wh= 5e2؝/M-Zyꑉ 39ɴe=4LyasdKjk`6 i"/R aFRhEݨF7t)""""t%ĀG%ْ#yQi4Y;eeksfGPST}98`9@1<ϑZm11 QeX{<S9)'Latg諒Yg)l,_ؔ zx6 n7'srv}GVfĈsm<8ao1_0ֳmpkVN[OPT6)ɶdvfd0uYޜXa&ŖEE^yZa#;*],>Ҧt ֮Z|Ac4(ZCQT.GDDD?#՚`wSR|nu*W\N'-'(i.IiH ZB[mG a$(p IDATB<wͼxXB%ҺF; X@ɶ%vJ2kjom|[RplU۩c7ga`MtrdXcbOoNNNS4kO^#ownnZ;]vth7ngѺgqMqD΂RB&kWХrQPLS J;+i$vGcwM u \.8>{ ߻{k1skJkK3}d1ϵ_kPE6 7[/T^[>5^jtw(}$tX!Ba$Bd3 J,hѦ;*jK̸aA9&i[8cVza5Avbm|Y25iMmIvg¤Ik:n9e_`aBZ!7uW'&NqCaMkMّJJv)phm3գ%hK4:b^AR-ʘ0Y+I()-Z*TvwnO7ꪬL9m9mjԏUe h5yyyAuyX>M>i݆Fma2L!BdI& !=Fňmć)(Ul pK8Q(ZN[tr0rm)HP)wo-B)V۽1FaaBL JnRk~|./֤kBK/ǚ4V!7umN˺ Ч}Mc-L X#%C͡DX"РIwP+L""@s:[FVm&Li*3jjQIW 5tm*Ǫ2ko G)).odx f؊GOYm G'54hT !60]]},$@J2[SްGgMAH&*T[",$I1[Ds|bSI m) lIFAݝ5r|3fiQF[&͚[[lLو#*8w9ܱAAs[Aj X:8wH&QIdR$?qnkܶm7ݛSԜNi‡[m-4M&2"IIBv%+vFmMfO%[R@S6+VMonMՋSU[ ݖMQMkgV>VSIUu ea*tw;B!NJ"WWXndS"!^0LfMUɫX$%%>K$ER*%811*FjV-mFSV{m3(NNB B]vwxHK$)PIWBBiքZB9rd%?͜=֬Y$)IXv66st%ں4էSngztHoa%7)WAttuuЖLTPbb}Mv|ܴƤ`ڢyT{iْ /.D]-C=z[ UO u mJUOg0ɽ>HbO㱪 O=NOͣQ?rs!B=e;JlysLh&iwZeg{ F#SѴZ͸#]2_bU* l5lӠa\9{KV_ x<(1$Z1`Ke7$r|bBRQQD-TUۂvt33()hfM ),IZ{M25"@Cv[:/.XgY rd7YIWkUS!Mqq[ee]eۤY$ư:Nu);m:mk! PM&&+ɶ]zIPW+NQah(5Q]Pc7LOL?@&T;VYmY[/5hR%,[JߊVV%M;e܆YTS4Ý.*-UuWe4 _'B!DNW]W/LLHNfl=lqJz[tZkxoc4KCt(gWgiq9uֵkc4,q>jny {MwJ^ɖd97TDŽCؐ& ӥݘ&0av[w(tho),K^=dܱzJm*P Z4J3_-eWkrU]Mo>୲ةlkMUCa2&Ǫ +)E)55(-5* ڀ ?&>y!B!O brx21NM>ZMfM( oQV=1_VK_KygŸ\_2qiytkr85T֜8qV; >j}&eý5y,+ ZwfeTU§IXUfu'#U=Jz y։'>oiDW}YxگgO*Oe(޵&66ְPs(FIIu\ۆ=(@@ݧC=W!B!ċ#=9QʡWCx|oK$u:C=C1>=)f?Yů]ݬ ?˙sQ0dDݾ73?akgV!.)ȘX|wOWmxiض%%66ɰbkyB!B!DNeTMڃ.=$(D.dѨϤY*km )i};g̢>vٗtF}˘_OkB0` hN\ʆ1?&3 F_tjP2yZ..Y\S]m׷1(vwɰƽeB!BT:̂qȑYBCQqqx* $Ni݊A5˾@|VoFLbK&yyC{glƍH Ӄ<DP< c֫4qjbq(B!B<*i+rVaRLH"rh2ՅXLf3Z&weLf3?-X/nOHJZTqzzy$$R8#B!B!O" 94XtI&Ξn2Y~>'kowTT9)CBT\.DL܉Sѡ!MRJndnn˥*l="$(ȑ\<(#9kE]|Z'Q⒣ S#FaNzC1!LB!B!ÔД3ZGG#ȈT& !B!¡JhKP\[*G"sLB!B!DfZBRytt(B{ızK̶J*HamB!B!^T)J K0'qJBܓ㓉F=ЪV }w")ߡq!B!/';N ,LZ"[= #6x&Eey oۉl&fHy5!B!E sW@6h"D7gg܍'%3%x-yIj6ǿdB!rd1wtxy{jRr#PpAG"ݨZ ^^hu9J<@MCB< Dwsvd<{Mgy8l;B%!>yvZ.lYOŪܫ3u| JaoԹ\ !QΞ<ː^C n˓D s{ji0b8bj%( ]zvBd+ 9+<utυ%+i\$]N;'~7C8|hwyb5R@~^ے~^^T,tA[,/>B!OT'Vp%N9ŵL*34 lPꅪ _x7L_<2GΔSY6>~FN֍[;t5zLcmD܎`dObp)΅0,%53N)Y5*RDaGPWmꥫh4jMX@}*BE ͯߍc\Js\\ ߦI& 0{lNGzPXGܸkK9Is_Cܼtu姁o="~Ր^O~_7k~&sujPN =y:WŘ׬AU>CաtOwl!DM` EJѣ>xxx8:BBB:u l# OOG-:@S%l {}vb0r=ydT*{vzFQ ∸EkIMz4m\]]Z( qqqDDDp1^Nhީ [x1J{)U"aߧǮ_dbk"z`M~^;ز{:"JAMAz`AΛS۩C$3v79}a9"Ґ!4 a]UΨ AUJ$xXL_7~)/Vس'O)BժUiٲ%ct׉'''7+:vY8s!'>>x+9- )j킙 ؼv ]zwEl۸fr9۟=ۭלQ?}AGZ 9MNѡ[F4 g/( EuV+ͼÿ f8_53{-'ZN̽/КnZ⥊y':b 1;N.[M7r$3yhۍF#9kWwwB2{3i$*Vh{̅س}-۷ϱǘJTiO#*2 V;DEDhvja7x ._ ɜ 9ũS] _2ރDEDG>b}Z{/^T2qقX,t%/XMٳ 5"*Tdz6h-z{]K-XO0GHan6['߰dR] uZH`&̜@zl| {^z̙:E ,Bߋz+1AiΙi0 |0}#sCNdɳ=yV3m4\x8Gnؚ\ DX(QxcTDԽIb,3$(DmzXT맹]9tˈ ҳ'Nq}IK_~H|Z-}d4qhaGo^̞>^#Ō֮XG&͙ yjvzDEF3pbBOR29X?#feëd!;A!2Ņ~q3&Owt8ٮC0q`A~ȒyKïSDz~vk׸q?"nGy셳/)PNuvi s JTr`6o8CNSrynZ3w~/MPB;}O$elݰ 蜜uXmXDi;}K/Vh!$Z U0娿U(xbplq6lu\\\hjS;Su g/׸jכ%гMO3㇏}X0s:) аYC[uGRřd˖"mMC5޹w>F6}9pnԆ`k]綶^̹S]<)j57biz[%Ö[ʐ0/x|f;.ܲNBd!hѢZmiP1]6Gut(˓ˍ^+Lc:o&Jզ[q<U>֍[Lc:s&Ңm 4뵯p`a'UjVn]f9+W,ZAYQ IDAT̈Nv-K.PTuڰ޵˂9z `ֲP5۠.Ý;{۞4\:o)"h}}I&璞h>{3EZjT)㰞B0ȵ$] &5GQEUBdQލjժ%תZ*d7pt(YXb|Awp_T(Zh4r1'=ZAf]8z A+X,4kӌ/LslNǠ{ZY~FOk]*tRX,[wٿNNNlX0vvJMXc'20!v_+]YLz* *8:\jժ_')ѳsBE 2um^Ja(_<̉'2~*}ݱ7cW-Yh^TV)ͱ55>Y|݄3Ej-{u+RW'&'%cj5m:ς;/ni7$ (@{ٻ,_}ؕϜ{=y8h 뙑8;;/Bd|%TK,_}* _c<s͚gBզMBdȻhuZp% yzzݨ @*P=.6㇎6ok9͛bIܰ.-97*2V1ĥWXz4׮K{~/[.-d4ѼMs|W^zl߼ 6gPbس}>>Zg>м9M|<2s?J_ź񊧗'6!,RVw0g\x7u^W?#TY{א֥+u2>xU %&Z[=lCԝqwwo7^IIILc:HE~ 4ݨ.Z'Rl)?1<* о[;oκ3n؊hUV7w7hԼ5t,g< x϶=\N=S%;B8b]ZmNB}k_8 E}k_ uޚL\w>IO]":;ǝ;k2[4~Xҿ͙:V_}DA}8!I&*RK7oQ< wσ8y-K]%l郦F ,]b Ν$,,0n߾MDDܽ{77ζ7Z@rr-ڼy'Op(P@% (7wl??r#_tQ&l#>䱓7}e/qxxypqn`6d akWg$dًz73Kf-@Z"O@n^ɲ2L&^t׾Zj5S! fiFyq&xxy0hOcY4{NJJ Ox6iل&-ez~7Azpvsf7wPVUlDA+hѶ%ǗQwUK}6NNN|;[[ #?>ÑGiݡ~sN];Ocn>qsh4ҵW4Iɚukоk{V,ZA}fw^nPW7WΟ9ɳ F ehөc;1![vp-~n#?Nx2-3oA~$xۀeYtОQѰRj1R\:O45kZw{ը'OgJfv͉'j)SN:QL<<<2>H~Sؿ?˗uRNs?rqua4'V,Z}9ݵ*߾M3xǛ1o/ &xipש2J+jtz-ݵc×|e:a*S'LMuͺ57}dϊ(, wwZoeBdt:/BE !Hݵ%_*~+l/fcF6oLuQ%J|}Pl9PRdIJ,Iv↓X*UмysB<6Owĺ8qol2M鲥iԬ!5ְ; ۽iѶ7pZjhgԻ'uժFيe+o]bmѓ~!}ټf3a׮bH!Pգnúi_}!99 h7o[ǻpn~5.BD.|?kW#pocL1EɗJiP'6NotQlXGOkTe{Zߪ}KJ)Iɗ?ɳ蝝)U&Z|Eoi&^!9|mPW^}%sxwn?(o>T]ZcKOԨR"##ٲy3W`LIqt85jբjլ̞ePPm"Ⱥuغu+: Pn]3~pdX8s [n*U;fKR14",V@:q믿ζsFMŨJwGut "8rm'"R`l;u;g0B<>ZQ|+?qQ M::!Dɓ.]::OէZm=zZ_C{Y,mFpp0mRVYjrQ\9BCCY~=Gjժtܙ*(Uy̚5ׯӤI6lϣ… 3`.^oܹ3J-B!B<`AḾ"d(5kb2n]7=EQ%&&$, z4 ...Ի${a޼y)R?+Q>[l!((cǎ1`\dS !B!s,IPrmm()prOfݞ{Dn]=GGGsUۯ;wdJNӡhHNNNJyPB-Z@ϟHQ-[ƍiݺ5 UViڴ)ʕcƌO 6 G&B!OD bbaBt7E#K/޻l>xE*TxCL&Ο?OHH!!!ܾ}FC(\0%KqrrJk41Mtt4o>pss\rTX *ԱgEQ7o_~Tx@̜9cG谄B!"|վr$*GM V-&0wUvIJJH"T^^z fUYӡpuu%SΟ?ϩS;w.& *P^=*VFyU-[޽{8p Jrt8oԩS7n~)^^^K!B!2W`hU*Q_H.MŊXv2p Q'r7n_~H| `„ ̘1#FPmB!BCBdL6JY۞?|bE- {eڵDEEQfMzA9pkc Pqqq8p[qF4iBfTc4lnmN^o߾l޼M::JPPׯ_GZɓݻ !x_k%''3i$F!B}ȓ'õK+Pvm6mٱc}Ͷoٺu+;vԼQٺu+۷oy1|XG#B!DְsstuJ&pR-zvT~2w.ɕ+3mpN<߿M$>ɉ֭['رcGĉ(BժU|3^Oj8r䈣CyDDDbIwMRR/_&))u;w~c !CQn޼ɭ[...+W`4f0 l6{۷oc2P5vjʍEɋD8:!N/O?qx:oϜ(//ʖ-2ϏӼys,XԩS}>}%K䔥-ʕ+˗1[rQNj׮M%駟0L5tЁbŊѢE ,H^x3>>mRxq *D0 ) !L>@5jDʕX"3gLٳ4hЀrѤI ,{Gdddu̘1@ʕ+G…ٴiS5vRJ*U JžR{ۥ7uɫpp8I&Eay:LA].#GUl4Jū;ٳg_ϲEFFemP(GL&|MF ~:k֬'U;w}Gbb"W^ܹs\zڵkD>|8Ç'66m۶c.\Ȅ%R>GXVkʧ>T)G,r5ٳ9{,nbɸZ7bʖ-իWt!!!jի ӄӤI>aaatؑVZq .\ u UeV|w]t"TyЩ"Gdb.g66m;v_~ܙ 0^]wtO$00#Fѣ,yMH*+Jŋe˖eРAiEFFr9ѻ cҴiST*5jԠ^z<Ybp-ӽU-wtHV u_z::!D( j'NJ_k׮֩T*nݺe@,_||TWθqՕ~/NqssÃ?0{xbf+,΁1}e2Q2F!&\LQoN:СC)_ȑ "ƍL`/.z~g~777qs"5V˒%Kصk4mڔS#_SZ54h@ٲe>|8[n}zyyos<'oHklj팟 l2OF GK\l1̦ !^XEe̙L<… Ӯ];bbbR4iZ+RF >s;x_ü>>ܹs37@RիW;wrIݻ\2bԩ2~xz=ݺu?w`"kM0s͋b49r7vt(9ކ .]ī @RR}5u&$쭀LA\7QWkG֪uuowںuZ-.[ܢ"lA=P)(0zUs$JN}i޼9}ޞx6l@.]J)iѢ3f 77|||pvv'}q;7`pb7SL|4i4k{z>u(1fH^0~ub"cݭ6;qZ붲l2%[[[^z2i]ʅ+GC[wJң$̯+~}ѷI*dժU,[cҸqcR){E.|owߥsXXXp=_xGfҥ׏#F͆ ^{PqttuBRalR\w1͘Ͻ;=nYÒǏ/^Ϻ>j=vSvc=pOّp1 6ZЮs;[Xx^^+d ]+ؐ#3vXdZE/kzO b^̌LN˸)c_ tgcFNY UZM4I+؃ ƍT*022BRƣGѡI&t奛,l2h:MڵCqÑMI˖~Umllӧ۷oĄ,bcc%>>(0xkhhT*EOO(@V.Z9Zf߾}E__J",,PˆFR!033CWW]]]$ 撞NZZjj׮Mڵqww7M9s yyynݺRu҅{f~]6/ ZW^ILLF;!C:U5k4n(X8b͋m߶mbݣ)Q6p/Gtd026eVr]n^y+p"+@O_ ˰i@|L*,kXM Ŷnl۰ TJ˶-%a"f?u=6[8拉_EZZ%0~x5j͛ @&DZ+<ŋo,\t7oz*jksssFQ annΩS5k-ݝ={xb,,,* ڥkE@{nlj5VB&sU""O\|~zzzմCR]PjՊc౜;y:tyus?K,a[5,V%| %%D_C\AV,l… puuwwwJq'OoϏ={bt"Uڒ?Zf!9y(֭CUYjj*7nΝ;hiizjLLLooo,--111腃*8.\ȁڵ+Ç?`222???077xqqqrqv܉=M4YfZD`` ;wݯ_s144d„ L0ƴcʕ%722b…%>| +T(k %t*„D>9yyylے_AAL3+0燹|1㿯<;u6$=Jbiڢ-l۰ 3s3殚K3￟}6GLi>ӿ/(59Y_ʼn ƻ FA<>w5j_>wٕ8988PYn]ŗ lWw_AV߿?Njmb׳5k<|8p>ԔTZ-W/~3v̅;yZΎ,X'NbS87ngw'};ӴESL%: oCh>)`beѢEH$ƌ3ˋ͛L@@oO>yo߾T*L{nժ S'SҞ322t׮]#22}}}ׯNNN -Jwe8;;Xra]%-[y%VE[[[[[lmmi֬Px"fxyyѾ}˛B`Æ ؔ~+Mxj@YY<{H0j5.,X\^ͦ6%sWШ]a?}v>b++4-?_Œ_ $BAǙ~b;غ~+c'TXj:dӞM/ oG tSTػ7s/mbJU4lҐg}7% CF <-k`?Xz=yM6#JbD>Lf*L*Ls-|[|2drq9.›I|z`jժСC_D"iӦԮ]+W2k,&Oݻ.ATmۢ8wȑh}sP._L#㿎si_5{899v4i҄zj>6HII! ׯرc]FTF zI.]8N5ow/xЁܿD P, N=Ew{kfҥ~8ttܳs4jLM<gJl71"(ݦWcn2rXA! )H|e$'r#578t\\Ѡn׃?ݹ[$~$KdЗƔFOeEJ%K'X9/2xW 2~x-[6mZ)\tÇk9Ŏh͜l֬Ӟ[1^ ޽{ӷo_6mZlNy߿?999X>UjǏHj?~e]Z5@dxdŸgFўy5Xx{;߽uX0QWOAH [S'2wiiiőǏIJJ"%%L222,ZDbhhHթVիW++*HGI^^˗/֖/Ye2j(ϟϚ5k7n\CV; Ҟ}}}AAis౱p}ׯԩSc?M"0x`oβexwdӦMѭ[7Zn]ZZZҨQ#Xhݺu}#V^B`ɕ!o=:=3%ݡg8_f̅Ě̢&%&f/m`jVH+=5M.)#-lΞ8[O,Jzc Bէoç'| ]@F] \S3S'>sݓO^\ϦeRjԌ9;@P|eggFXXQQQĐVchh9ժU}}} 100(Z6*\`A@VVdffFtt4W^%99J\.{{{]K7-o"XNoNff&cǎHÇg޼y;vv=Rp:txi֭Ҟe3f z %k\ǩSpqqaږ^T*wښ;wr}]233Yr%rI&UjpȈCr9/""":th?BVsiكGȨAx%RA#qe"(j7;e =oT< 5ljcfnZg#c#%/GA}Y.T纴t9FYe?JJ5x5aM/ckamUgj_2J} Ϝ?1 4`ĈU&XƆ &… z1ٸq#sEGG/Ν;@ T65V&=֯ 7n'';}w4dr>|nYć w౗{=w,,%(h( :7w\ ANv5s.^>%7?mX(8[,aj¹eodrn#nX<{H+tkERr) N8w}nj3 ]v|L>=zV+#[nG}??25k0m4lǏ+t^"ant޽\ҢsΜ9sKxx8rQ8wUvf@ޫSPn:={g>OLL k֬Yf 4͙0a*~S:Yj3g$!!3qDyƂ kRի@kd_|Ś5[z鱲Yj7{{r椤b ԩÀ|ʂ!Gflܸ>9p%.^Hbb"L0ww*mfhdȓOs.-}AAtiѕ6`lj̝wpjub7n'n=}{m\ʭk\l ~E7?cj2yDŲeE3b;w{+  '<4(\~14ns3۟IO$<4hE1\/?wTl_//UmAxQA~eOǚ5kx!>>>UzMH$EBCCٱc_}=zSN(H~V rQr__M^رc?s'IR?Dz 6mPYj*0aZSɓ'mm`>fffTV sssY&5jx#yyyY7nп*R):tÃ3c D &**Ν;W18rs禧Cbb")))B@,(^\SSS,--V={y!:;=J%qqqEfbmm֭[椭zzzann)ժUrjprrq7q9s& BAAA*:x<42֓'Oy&7o$""4d2VVVaaa+zzzQTT*&99nݺEPP999@ݺu_~X{A999̟?$>-TF &Nƍ={6ǏE#c`ܻwDBZ*옵kfΝ p]޽Kxx8iiiHR033R)PUB IDATTdddÍ7HNNFRall3uԡN:/$Xݴ)E:r$f ffV.昘мystttZnn.?޽{XH"{P(Xd _e>jm*mrr2:::T*KÆ ڵ+NNN!#RҥKٳ]vѼysڴiCjP(\| 4_~Ej`Cb{(J6xLLLWPp!,,;ƞ={pwwˋzit`HH 6LccV6\N6m8r;v`xUMnnneO㭗\G A rm9yy=B ??mqPW_8j9TwiZ͕+WطoԫW?:uThb퉎l۶={ЩS'|}}+6ŋ9{,cǎz6H*2p@Νڵk?~k'c+XrYѣM4)&,Rggg۷/=zӧOSfMÃ1cRZ&(ΞE6v,9sкxŚ5_y^rwww۷/ٳ[SSS:t耗F>`O>MzޚUZj e˲G !++K|BAzz:U[ ؐ'!%)) \,OJ,U '66&MeeO{{{֭[žҷo_7o^BI6m]vM?+7o,Z*D0QC˽ŸZݻiԨQrZhW^e׮]dggӵkWڶmQlP/hrJT:$ LPPѳgO4hcgddp]Fjttt_>ϟWk@-֭[4k֬!yٳkJGy{.* ʞ ¿]-; "::_[ݺu cScLu o'm6GdpsMΞ=Kz:t(ժiק[noߞn:N<ɠAO&;;?? ;OOOvZĊ[OMMeٲeܹ:gYw% |tԉ}l2RRRڑq;rlTP(467333Ǘ_~I:uXn˗/'11 .*h6j֬w--k=WARUqh]죏Ю_mggGYa(ojC愎޿7^2V3ÇWTJ?qFoEA*DF>:㣏>bĈU24]]]zԩS1cGEVW/\@fD h۶-< !!D GR[m믿~JǎLqSLFǎ...WU!Jqrrݻ=JehlH b޽vM>m]<~mTFA q+s@M*삾w^lHATִ[ٳ=Rd֭\rnuWAx%yyy,]cǎ1dXL^D"m۶L8x~碚!!!Z}m"HÇ<&j@*AMF^z 6Ю];˵rLٰaW\yG /(LMg(iӦ 'O&))sPsLL o}WBCC+{v]gە'N0\|Y]}}~X898YiVnn.׮]cק#u=VAoSZvf,^۷o,Ο?̙3r {aSS奧믿'|g%_=-21qDtuu9s&QQQr4r92ʜU\7R|~7ةl666$''QK[K'??C#C2%j5kS',!`玡 R 3n4NK<|%H$j׭>D!zA*q;s.<CCCtQ?ޑƍalѱ'['uobkO yy<232eK67uAxyYYY̝;|&MPU(;;h'kG3k ;v,6l`ܹL<; _ds16{k"!֭[GӦM߸@ӺtBzz:֭cҤIXXXƍhXϑ{{\B]mutt9r$[la=D,--5zZMbbө{#.2 r56~).ӾFWr%Hu|}rt Ҷꦺݭ#GrLG|D5k׿kk%PYX[sprs'#-< ƿ|і5˛?r # =j@@cScښڂ </^L^^Ǐ:Ip:rTy9ri5\Y~=cԩXYYil|T*阘hlܷYjjkuD 155H'\Z͆ dYӧl޼ &p>B|P?f  ֌d <\ʕ+7n\5^Ǝ[U- pC73~EҌoDdxߧ1./\%0L2L46fglJcL* PttU Xp^c3O,:jPC 4 (Zʕ+yqD2rXygggUURb ,X_|666F&3,JH|||^y SCW4SxQN"&&Vx漼<ϟݻ56L&ߟxN>>ԪUիCvv6>$44Ǐ~:vH_80??7ҰaCU=Jlرc(J Q5j LLL066FKK(%l,J%iiiH@@ƍ{kApp0DGGCZpuu C.R!77dIHHƍ:tLFzhڴ)nnn … 0jԨG5v\O*'OҶm[t^0iCC v(QH"@.HA?>`ڿ5A:s 6lۛ޽{PZ-[Ã72k,Ə3d޽tYe._&DR}]=ܹs?lj֬IΝ|XYYѷo_KJv͵kװgСxxx@|4i҄&Mƙ3g8tϏ֭[WJTVXAZҥk'ɓ'իK/y&J׳gOT͚5رc%cРA 6 RRRϜ9hۮ]S)Sguƶmۊ{|Ǐ\δic%r[-[>I6UpG~2l0tuut֍޽{m7}tnZիYzu&&&=ﭯrrrD0QCvb= Mڱ%"W'U;՗i))T - Pŏh6WK2Q*ŋ_={Ү]ĄѣGi&,X)S)uۣG"iJ)8Zu1Tl\"##9pw???7orjӼysRRRشiOORѣ:t >WZҥ ڵɓr-%%%e˖a``AM6%99722Zjì/==̢ZMw>sCDDtؑK[ݻwd@fff&[n~(HϧpQ^ŋ9qgϞ/(u.BCqy ^^NN;VbUÇ/طoо}{.]Z,M޽g׏ׯ3rHC@@@Ѷk֬!33( &322ɡzuQTe 6O%ّ}5H%XwZDI?|iNk8e]A eDmb؉ TM]N:R L&-ZDfffm Ǐm۶oȡy_~Yb#??cgѦM $ԩ~~~lذ| .ȑ#tޝO?\Oѡcǎ|gXZZh"_bLI|:-›!77+WNΝ_{<---D"a%r 999tE -l,ơ7RɄ 3f k~F(K.sV\Ijj*!!!/jNZO355e <Ǐd224wTTxbܘ2ekuo'a+_eNN S_+\ѣ2755ETSԍĉH$n޼ݻwm}233f͚Şѣ}Y3d?Q.XP4ĉXZZ}'$$0c <<+=Axڵ}tuu2dj]|ww ]Wn޼֭[bذaF,7ʊ͛73k,4hŋiժzzI&888j*.\ѣK\^V\\۶m#>>ЦMu+{ԯ_ϰa^h¶oJaKKS@,szz:2'Kvh׮]u_*GWh-Y쭬}9GPPP䍊"++ \Nݺu|}o0\\\*{oh&]A]3+2o IDAT30/VHA(K||FGG aŨWuZMbb"۶mcС@Ajɓ={6>"##ٶmnnnL>vqa=Ϝa#|ƍ05jwf3N:P(8|0[lٻ︪Ͻ콧"*Hu2sfY9o#SBM\%.\q\8;y/\O={?vϕ+WYf^~e/_Nhh(j?rwIVZ|>d |vӵ=s@gW17Q-Pysx rrrXz51ŶW*(2:δiJL͌v_GӚ1u(E?h%yZFZQVM޾FS3篓KסrϮ7r77u(-a^Rɟs vj~h~kc< e%ٽLB.ƒ {+*+W0}t&O\)O+..~ 777i۶QϧB%8qj5~-rQFU K{޽ 6AzS3Xhd|̚5W^yj!!!ڵT<==?rRs#'X%V -XL\8²xX"//FyfvEnœH666TrNGll,-[4D*2he23uH(AQIA8Լn5=iM1|T4|CA`H$ԯ_+Wk\BAff&T*tT*d舫+O^qqi%e]q]222!33֭[o>j5VVVXZZbaa=...uejj*ի&Zjʼn'W^1u8Edb9V1̙3Ipp0r@8r;w6qqa\\\^邰.ܪ#G=QJ\NTT6mM6%NhȠC|ϚǬ(/>[(;Z|=iyoS/ʟC˧Z_z?y p_qYi 66_q"Յ  *Kzzz9 \t7np-233VaXZZI@h{a0UT g꜡s="Nܼy7or j o[ZZbggǑ#G CdZ-*ClPLJVJ@@>>>O|B֭ߟ+ID>}9y$@$ˉ##Gŋİh",,,3 &t޽(odT*{CfQ֍P, b| p0qѼyso֯_;2T*?i׮][cLbЊiݛݣ]C +X(A0'ެT(Fd*k@|'-УowS#^`Qsy∏'??OOOWN֭R 899=tV%;; uWJ4uP~w~~>Νܹs?B 4ipuu-U~*LqGEPJݺu_>jx7or9FmUJٴi!!!f=db9Uj";;/r-rrrgbcc+0;v ˉx$Ƣ@u+>믑3犖*JË}a4k֌`S^&6n܈E] Ġ"JALI7ǟvx VavE2Q$55Z Z֭[QѵkWj׮cKWc% 77",ҸpǏg899i\pÇsir9jբ{ԭ[g'䄓4jpd9wƆp5kAmFPPi+"Xv/9{, G; n"4i҄&Myۛ˗Ӱar3۷oK߾}͢Qhh?}fem+{V(EΊ+7nbΜ9Áx뭷wbr{&"K@ LoNOMͷ|XHlbriysu&5%aBL&ã\Η֭[ٷoDEED ;^QQtqI,6xxxIVVfϞ=l߾-Z޽{Ƞvߟ u*$IK۶mȑ#تWNSlPs^29$$$۷dP>Ltt4nOT*~j֬Y,qjNt]Rpń G3oVVh4b >}0k,1b,5. V\I۶m 1u8fG ZD BII~{tz|?+YKV8*M_Ixc0Xt:{e˖-K/Ѹq*S'u"UG'\\\СQQQ9r͛7P( bԨQ>q,OΎ֭[Ӻukn޼Ill,?UTsOTՕڵkQYfdffw?QgRdʕƪ^gժU1p@OVEe ?Da[$IŖ_[[[ꫯ LS(xyyѻwoSc^zgox5 H 7S0{mogrcG%s%;˸A*Yܽ{'''#55͛7Ӷm[&MDӦMG` y ^|V~%-Z`ɴk׎+WrJðSZ*C aĉxyy?xbӟG%,,sOVZ8::rSdYrrrbȑ;wkVȄ^ٳ 6L>@G+Q}$$pښ-Z{nbccM)((`0j(6hd c2ɪ=?GT2c Ο?_&=-oooѣbܹ w!55ЯdrIS2gȨQ̉Ncڵ9r^{̝sg $S':oNfΜ|ssINN&99|lllcӦMz^xS^"JŒ%KHMMe„ F#WA+(A(O[,`N4Tl۶͛7E.]]Ws\wDm"}%F(L܍3uֱh"wN֭v{Ǿ}ظq#.\`^t%1ŕ/ L4cufddd0xRd7%Rɲe˸z*#FӎU^^|١=4|8ժ=zU˗ 6kNԯ_ߤ7fN:|3k,L]rٳgʘ1c*t" >>$"&֭NLEDUV5qj޼9od…8B ̜9I0aަl8!0$n_&(Ax<Y$uO},MoD]O &ciiIAAA3&&ݻw3x'J$8s =H_DDgڵ\YZ>>>; ͛Ǚ3gORRR|xyyhPD2"?F|[Ba  ֭ᆪjժ;R,->p!!!xxx k<.iStcƠ im 4i999̜9xE^̛7 0a\\\LYViJy'Ҳm>tgj ^-j+LAҨ :%!]mǜy\ZMe 3[[2>~8[ne<]ODYD=UO'Ƌ/ƍMzZKKK Ddd$K.VѐW9Fh$'': Q\QkѨQ#V^M\\;v$""d44 `˖-XXX0x`6lhXZRR/^d>X XtSs "[ )"°/&Mb,Z{={ .vZ2d͛7/s;Y߷?tmO/ٙwxD BɞOaecI.O:S8ٗSd  YY8RRRXj/B~P#@@UY2DDDpm~g["Iݻwё_++stgׯ_7u"XфѠAnʟɶmhժۗ<8={Pմiӆ(7-K۷ozԬY>}V @תS&Lz3XYY1p@ZlɊ+/hذ!:t0ە+Wܹs4k֌>}A+VW=j|:1i zk7a,M BWָr-oN=ֶdrMl쬨Ӵ:r߯y*NBsXۦBёr#*E/3ba)ɽr^L=}%ͰvVhRM:2t%鷳wޚkgHM v7=8aLOq+z=k֬ߟΝ;?rx]<4Pꕨ$Uydbnn.ׯ_/Ӟy=z !!~^{̎[VP*DGGckkKڵ<سphmD2[nmۖݻwsNlB:u nݺeST?8Ο?--[$22;;2=]|'N0f̘n#mIt-] nnmUĉ9|0111L>{pԩ\.X8vG!11&MT1hE( 1Zg)˛g!L qt'7+wOp чy;NiJ07d}d܍×`llU!8>uarj:oGL7 ׯ| gΜAީuԉ<-[Ƹqp[V#IVV-J{dbfooOΝСODGG $88*UPJ^Ebb"\tk׮!ɨ[. ^z&+6&NAXXuy^^6oFN-,{^)2ҰL&y4k֌'No>,YbL͚5qww7 synݺ˗t ΫJ*UGP8ݏ:;qc2Q(Q-³ͫflц>ū+ZBұL'.Ha-ظ`_~/y Poz6Uk{>c/VS0?Z5:=S)wz4EXh j>ޣ8$c Pqrr"11񉓉:-[QU*D+m?l5kFLL 7˩Ƚz"))e˖;r* ++Ƽn:f̘}3\FR: L ,,,hԨ5BRqΝ;ǹsؾ};Z\]]qqq+++EJZ&??TrrrPTrY&QQQWRl޼ } =n]Xܦ G!"%$~Gwرc?7ccc5VVV(JJw^ˋ:uЮ];ׯ_)OM}|}Lڶm[q^B!eC[4|}z:t_~ɥKUVr ̙3k׮@^`7o4>eAәEWd *kkkBBB )|h4ܺud233$''Bah> IDATkmmmHt::th]} رC>JN)* F?e Gɉ6mЦMz=o&99;wA~~>hZŅF퍟Y55GbЊPD LonbcC"%%Q9b ֥cP'=lQ)XW;G?C[p7=Û0N ]]]ѣk֬!((;;;,,,(((@ӕX񗙙Y͝JadbegaaAjJCO_CZdbjj* mڴYfOvʞ_}-{~]/I3"(1uysi8ؒ[\] w)~P9=`JYk,h=+܂2OEX(-44UVqʕ6^+bxLB&;,%8}t̙S5kТE"?~]rq6o'|BZZrnݺxbC"jժUL:BAZZ$VEV3~xvY$x%&MĶmh4XZZҹsgfϞOX̙Æ ؾ};֭O?ҥKt҅իWpQwɓ'ٴi 1wޝŋK*fffn:͛AAA\t^zQPP@ZZ* -1MwbJ"(%Lƈ#ꫯy+}Ӭ,,X@ժUy=s$!Myst*Y01hE(OZʛKeFkV~$%rty&,)ttD&q=6uC*vL?gn^4Ɣ,=h5Zl,~NAJJ`` Ǐdbbb"$^xݺu#88S1cض,FEjj*=nnnXK[o@&M?~<3\֮]Kn݊x"QQQSn]Ο?ϸqҥ /S*$&&2|LoA-h4Eȑ#IOO7O?Ddd$oaիW3l0:uDy78}4~!cƌaɒ%x{{G2w"? >T"55Lqrrb̘1̘1K2l0,-+燞,XoVĔZ.Z~nPvĠTD 7FWp`ii]F<^_G1G(Q/ʼ"%^1iUcgh Ffֈ_jT,귨Ie;^ϴK B^n4k9[Udd$k֬[n BExܰ+1գ^z ۶m3\7oޜ۷uVC2f͚g}{ Wկ_GGG1Ǎ!ǪUhܸ1֭_.۷={6E۳rJC͚5cl۶͐LޣSN^EqyFƍYf 9899;vp)YJJJU J %w%))瓟o\JJ ~-= {z"/d|~ ߾l!Z9Q _+D"Q0^t_gۙ\dt\Β:DAs*o9 a>t1f~( >^߾[~F y-݊6hYoՎ GaimOuݦom{iEVϗS{hw_gRndpJg\a_= FִiSr9jCC,T-d|{,dԮ][njOOO22ɔΝ;i߾=\z%~~~;Foyd"r9j*Ν;ʢ[n\vZjqA=Jt;vTcEWMLϏ'2g[^{ ( ,Yjժ1rH$wE֨Qa4y({.#bЊ`D ʛ*]D}ֶ< յٟ~ KRyu56Rʶ4)X`/ko"U0N)~eǙ1G[emmMٱc-ZxT}*5[XؔX];;" t,]KRY{I˖-?o=\qTdddP߷Md3`ĉ8;;3{ tl۶yo].KV8Աcasp[YN!6ǒp~%ٹW>m}=Tc`*lVAFE*o~Rֲ-O5;'m>˕ӷiuy^D: oWZ ³Ɔ}d6lo5>s"C>|}}]6[ne&#ٺu+T^IuV:^^^P( PΟ?Opp0r['dP*2=zPn]~' tB͑$^USPP;ߟ>ooos9"]~:uB? BE (6.^OFZ|1Nl\bc5p7넊ʛV ;>nPJBՑZUrUY0{^^^R(M4L6~C,IU&|W$]nn.:\S/3|pKv+&L0znߟQF1w\xR VVV޽T;V,ә1c1$R2]n1jΞ=[lȍdXjժŔ)Sؼy3cǎԯ_쒊8mۆJ_78M9nӧ;tYt4l޽}2?~V~Sp=rnjoC7kLspc_&Ihx(..A@L.[N kk|QQ GF6m:0fRMl#%%CqmtzWHK2?6 /oo5m?m@f$m*e)))L6 &[Αݻ$$$\c^^/^F>\rJEݺuK,愄ju1sAܹCBB|駼K4iR|@ֱc8wVVVԨQFEpi̝;OOO fPL@JJ 7oNf)z=W^:u I^ѱxR#GW"3wJrM ԩQhQN|+ ]yD=AVvϝȷ`짟Rn2;nyK %㭱׉Xj 0W>|Wzf<<lm9|8ˊXps8*u~.DA: ZɩS8q7oʪ䞅!559s0`\|ьCY(^^^ 2޽{s!Ǯ]^zԫWիuRɅ '>>wD~ KKjcF(\x$+̦YkhQwj,WȖ4?@իkг^{3\*fΜ9 /URhh(GC&Qu?Nlj'hٲCy,(۶mˁ8qٔ?[b6DD 9::Ү];ڵk9}4OfϞ=d2|}} jժꊋ#ˢ 33Tnܸ͛7IIIA.Lǎ 52_GF0mh(ˑ:u2udOT_ ].vCjhs3q|+V, < 1b[o6CVMՙ:u7*B$fO>i]o 9yc;5Vrvo}F98:nQ΢E 9<7'sT"{pU\5i=^M6[,% jP. \S[b&1uF5xp:w̓\*+*RFzp {{{Fɉ'X~=ӦM# 5j-24Μ9sUAǎ#//pSXO&ƍW^t)._̤ILJ"(EjըV]v%''+Wp5C1''(\ikk-Ջ* FB0(H+DDDH5*C=?<_FץY=JVQ>'Mg"#]g7L̓]ÇFSat͚U4oԡTjGg4! ԮW+\Nkg-ܺs3}zoBC~&..Ow0hGfYQQ9uJJ20\]>%hdDن&6 b30MFU nTS{.{^ emmpWK>W˜/̎`l~*9eLhk$ =g}޽{iժӆh6bccɡ]v) ll} [nZ h觵n:4 =$̌`v5,l*;9m&~ )VLXVJ͎l<+pLyK+Z%4è233qu}d_nO?? ޻6FP=k5<׾}]\\MFYX8ryGGk^y ~~.?tu`HfXfSb|F 'ǒŋaeU^.{cPSgT'58쉲 3$ f%ֶE+8r2s=?\'&ʼsl)n`j5:/OGd8އ• #{_&08wTc*gBDuz"VWyՇOӓnݺi&֭[[m#))[ҫW2O薇| ,[{?~2zh]$I#kР[²3+A+!IbPhAx:zǭT;q&={ΧV-o""jeYLāQpʕG((Вɓ(6nH&dsIJvqX"3fl…)8;RPÀ"1R+W)1˛32cxv)Wy^ΐ@lbS Ezd45|m_߁7 齗Y.ixg(W K^ѿt!Dx}z=kfm<|kxկ]mZȽ"wu@V tno+u(9xQ ;g( 4QŠ+ /d"@v8}4K,a̘1V^^K,vDDD:'w_z%f͚1brϭ[Xz5:u^zD"(ԸqѲ ˞W7uhO<Be'J|_L߾|O 7Wz {06h5ۗx|ERXx1rVIII%NnfС̝;+W2`Yp!kצ[nD2Q* GGd+W ݛomُ?"#{2&"ϊgZi߾Ο_4/6?1'Nhqq#(ȓ73/\Ylkkɕ+\Bzz.婊fSPf~?ѡ+8k3a-Yتmhmkl+-:M9/H21qLhzgX?oGmҭNjt,d##F?rMK~ IDATΥ%GPs2d(H>]N%*WqĉR# .T l;::;0{l,Y°a*TBQRh"5jTIFFP(HOO'99$n߾͍7P(?~+++^z%M(`1b2Y-0́H& B# ,, ]߾e&"Q-e/%%I>ZW5ʦwMa"ϯUjj.w7Zd/ߥ~.o֢@~'bkUWuM#l$竕l\kw5wRofpu&FFϕMNF?ӷyN$(d⣸⊫j\#Waww-nq38^Ԥ"QUӜeUgחc2{l{ V!&!gff?T*5j鉝>}KKKΝKAAa;[[[iӦQ( 0솻&%%pB<==5j''BH#?xȑe=רa ³H@ B pCӳvH=/&'_ l#]Z57 W5kNEp Z44aBڵiԬɍxmo:vLAhذ*v]&tOy HC)!+-%G(jeHy5?b@'d!v܋LKV3i?)9:c+&&q/}]Uqpʂv0f~o5tKaXZqrDoE#G-]U}݀K#b vf`+8fSnƜsl&0f(*-q{U䢠 x1s?syczHI$E3W<3JCQnYx w>>Y???4i/R Kdd$ի4hЀΝ;RsLw!ŋ4nP ]I@@)Vn|HHH]&))MӰaV+Wg=70-_;f4/@_I֤0ܘ"FVFo@߀OnʏԪSz3Dy_Jލ%3S R}[>L >Yi gaO߸>z̽{ h{=uر:[^shJ?T .\kkkJ.+AUT6mrTgPQ7>~kERTT/m%bE5/_,󘅝vu{Nڟk},Żɫ'K4HS-˚{<|]%]}g 2=U(O`%ah4AB/ޮ11ITu #gA%$$RSƵfԨ>W?SJMg2r*VRݓK3###u놟`ڵ |}}/kGFFٳgʢ^zeS뷜૯ѣ۷GR~}֭ݫOO*k׮/ĭ4<ȃ& ;FN@$w#]\%b&F7pC/~s˗?cB}ƍgǪT)׳X\F\\ 7ɓt7D͚>,^7zGR 깒W_mĝ[vp?rl"F|rƻiE3wJwرP\MJcc,XK;(ÆӮE80R'nc޼cdffXׯ@粤M6F|Fa7? d'm7i#i "C](\^x%B,2u'^ۣuia]{?߈L2kQnvFa;vuÇ7c֬ߥR AAص2))J,,طo$na8O8q'M+r3pK4kkk'88'OrQlllLL^?ݻwq7n 66ZlI:u<Ӯ8DFFr ٞ2>&MÇ9rNNNԨQ+P^J0_IKKZj|嗸nUڈ` *UBzq㲧=_]\߾ojFLV;36%K>& ;cE[KaѢg?n`bhh SۑXƍõǏߢkZ9|?y IgYQHO`L;:ĬLiysliިr^ X֯/w:,eڴ DߩSGG cP?‡FY/?mD"ᣏ嗛s(FynvsWsw-tNb[;dH[mL#ͤ)f$ŗ=[N~+ٲe[g6m?Kz 3'9sC,^7W~&OÌYSRR2_l4 +C?ؘVZѪU+x"W^ĉT*ͱ G__LJ"##RIbb" |Çuss]vԩST3E]ehԮ90H/Y22ѐus{tVtP ccǷa6|#Ew_f+g`_`{ iELL],dFCOOFTT[ &nZc7(T4ej?,iּ\ǒRTk2Lc|Kynt1ꥫTV|SτSK'MMÆ D{7ժ9g (XϨsT GNR#d{==@L8!2ЅC4_=%H$$M<4@s(Oy)R2q6 j $|EKLGHH8迁C&& wa|?,;/9::H۶mQ*DEEATTHjj)( qwwe˖8::P'3ۉ>+kJ@@?ƍDEEÍ7HMMG&a``%vvvԨQΝ;SbwGD0Qq?~Vܫ={.XFܠ2 7ŊGK*U*KFF/GkK,k0?m ~}7v츨ɓtMOchǓ':u׾FN 9p g,G;=v2, oe}|6{!<5D0=WZK^OhC'd"# |{FFD.78 %GA˛="'>u'Y= 濡/r$8bD`dA=1~~׮=֭lߞ3[99YIDD\g`3d`ff@j_QQ r&AB{e* RA2͛7_ѵkWlmߟ sq emmc:Xx <=˞ǎE3kgn؀˫pܠMrg-[[Sʬ7B!'(WΜGPJ9`ccJZZ&IIۛL'=]D54 תmML꽌Ǒ"I CM)`3k>Z,JVmjj@6U4Sc9`y:]")8_`cY3n`ggh+i+.oiǂP2J|Dݙ1 m ==KڵJe*@=O믛DEk_z&Fә3g xko͛7棏>3/]"(#'N<+{ A~=j} }V@#}wߵGxz~CU033ܹ,ٹshtR)S3&M*}f4m!Mihmxq}RS38zttQ͡ &mrbEEs N켄D*Apu[Q+% }Fz)ӳ,˖$HX> #|}ԩFF9dرӓ庶})B3:ڐѣ}^=Yv?NqXy5ebC}S~P(H6'ﺌL D@<_=$NݣVb 5<s` o֣Ɣ3gk7xpz W負&\2&5S,iC?q 8#Gn 66/(.nHW}i.9͗c'zPȐ- .$jQ;35AƼcȐ`!5fQ#D(z" r~w^oNDDݻw/B>|ʕ+IHH`T^$ @HCegd={{?[ N &Uk9PhAxeGK'Mc_BXfgۗZP\'mJAΒ~:5z$XbWGv 5KNS3ܲeύ1_R㿛>Sygڡb3;]y(J'?=9-S39=;cRjDM3VҢE[%Ji۶-UTaɒ%L<|||JEypff&ø2bʔyBDA)e͚=+{g$#G\Vwc"= =EvJeqPF'_b"2VIOԢdLT-y\Ӧ` t;nR6s IDATа;ߢ[Z/F&Cvmda)5FK˥i]6c=t%ꑙY]6ݻbbEY Ǽ'J釹z8fvHO]WWhd²RY^NpUOa#5T;siOD{i:% 'Nd׮]['NСC\\\{k:i4.\Ν; LAAҩ2oQÏ'iA+BZ0O68ac]F6/p0k)s9f=}*g7]k\kjRh{7URKzR=1OςƒL0 䯏%h8|sI۞29ie%Hǧ1Zc%sk!-x@J#I/TIDiI:U}ӥBjtbĈftTTz"X еkW4hƍETTqP՜;wKFXW-DArssC-{23q x6L?zju|>}Xszrx]& %.DE*[Yr og="IcD$oR4dhp+%>ff 1^31sD)+4vE㣳>_BD)ٕO~K iP>TTFцdfN9_6lffť@^uۨZjظsl7*U&ŽBO_)66ԫ߀Fa`Xz %GRTWυ8u RCkH XXʕ+W#Fɞ={7ovvvRnb %$$p9IJJv >S4DAtG:s&OD H?HΌ6}?!5)恾9۰B W@gf<<8^(&AMָɬq&Nk i׬ Ĥ/%ݙu/N&<ըIT'Zi4I$QQ֛,HV'kmll,'N}JWy(ٻ QQ B3׈MU*e"Iחu*U*KJesG_"~Z+ RƔ5Ǚ2epsҲtdtԬSuj6#CkIkIP5z8991p@8~8b޽T\*UPre̊=NLL W^ʕ+ܾ}kkk7nLÆ 17/xՅPD0Q4oWQvSWki!|SHDΕ}1el ^(%hlE0|5q;?g orK8J&2n26xm1}$AYq>YƯUқ/9mi S:w0qvOhQ, yvyajjF-inY=-_^[ W@w0fM'Z]( /rtt$ ]rΝ;ǶmX~=쌳3HAtt4DDDpⰰjժtDAX z5|?7mdj@N.>}-)63~`7ݲ UdHEteYd 1)9[HpYW>C5nr,_Z&h4 z\ =8r<<h׮ÆcsڞԀƍ ֟*:ڐѣ}^=7=  R ڵkܽ{ϓT* ,--===IOO'++tRSS'>> \\\ GGb~BaDA J&#C^qoQfsq.uS|z0 _*9G J(vڷئMY Ȟyu mTaĈ昛n6]dr):UQ:W/7ynXzkU/[ap\JAxhCDiK8).T$Nԧ>N8(qw\qbOذӽvӿn-e'Zٳh1Gj323Ut鲀ӧa2?ĉm_X;:t >Lg9G}11I 77=j[{Obb+'pŐn(Fbx_oOpwOf <^afV|@7afHO|++6:½1'"PJxx1iq?^E}: *\3gd|w|E~=Z_yvcccsm _SpjLlPdI 1: pTRs]%sdY1ez\پ3n|O?e/kLҜ +dIEm R+E o?FOOFŊ?uKf&v @7 ڷF ovд/䉒۷c`oy"+Q"X@lݺLFNԩʕ#997ntRn߾d'D>}Xr%8;;ΥKXd GɓXYYN e˚1iRG&L_~9ȑPmbggF||**ٳ{-Rv]\9"S8 \]T$ކM+r(+fH%L7Bڲ.v^7u?k5ʴL$R V %˲ Fǰ4]N;8qTng Th%:s~{ \>*n}<~^%eX"9P,_^[Ey ov1ɬXqXkuطo[n3{=!uԨQoߞ#G_qu &п݋k۶m\}}}6l@ǎ8?aaaL<_~>12端ڱ}EA۶U)_FÕ+vN(퀔n]']6TFjD"دCAՆ }=*u(1)tfXa'c@CwJNФei9zK=LM ʕ *4.#oD7 &t6YbUVjժk-Z}$lْ#F0uTv!oh׮K9ʤIQ(RbcSӖ2)cD˖1 ˗ۏٲ;v -<ҴP^]xPX4! s ;qɻ| G0d,9N@@m 9q6kמag\Νl#ԨF`Ǚ3ˌTq7ץ(PJfA ZNǎi׮zz2vȍ1YOnƳg7RqUbcP^rr: $$$k]>E$ B"KUP:uJDDDڼy3.]"99ggg$FW\a˖-ܼyCCC<== lٲn۶ :t\.gΝ9rbkkKfG*Ν;ر;w9*TsθlMpp0vvv4iZ͎;8|05jԠO>֭[9{,X[[Svm:v숹yJazx͚5gMgl`֭;CӦ@5goN~'OuYTRC ys] +WQԴlz,m2)k96"aQ9ꡇv: ]q2?4pڵo;(kמ!+KNF 'Kq.p8ffǹt)۫1{ve=`m;s*; ˗gbt37.}.٣NHՋ_sz3n\v͘nuEy W:x{;21. F&ȅ )_R;DFfN\\*^X@Č gfup(CZ=|Bfxw&@rAӧOs5*Wλɓy[`_|)))-[+VЪUgdd0l0.]kr |۷/qqq\|sg͚EVعs'zzz5 &L`ڴidee1c8qzAV^:;vرo B>}^:eժU9\׳fΜ9y_kjtXX[)~y=v]h 9AAhժrBOO;._k/MvT4bF:ٙi;5z:וD& F,d:ˑoqtsٰ{쑖a'%?bp-^z|xyIgliWF+Sso;}Y1:vZz&.o9gu)'N 3SEJhYZb'ԧ}S "BZ՞s__Za/VP+ٰa+4l+P`b|̝;44h#ڵ+>ǒ%K2dZ"(([[[Ù5k.]cǎ9sU@6oތ9ǏVZ ֭ݺz:[[SFlXRR:']ԤNʔ1WĆ ߿@~) F/.=4<ʑS8I@م8cJB3u^OQ녧EzObbҎn^aPx}aaa̝9wid:'w]jܻ5yDɺuφ}v&OރZˆZ2zt 7Dۏ3'zHLL#88]2kVO jZbܸ-y>..`'>|8 ||ӠA4h@˖-AjW93f|٫W/ׯŋ駟X:s͚5l޼kkkBBB(_>;wfҤIL8QFѱc\_.^Hpp0ի?kŋٹsg`ٳorevOOO9yd`bHH|7:t(={zjd2xPPl޼!Cbff3~zΟ?ϥKػw/2~޽{\PLCYt.6ĄǢ0ҧv*/$btf%rf+P` .,G9dPڈޮŽfzzѼ'jut~Y C}빻hKe2)U"]+&ysObb`L! 9!!+mP`b 8m2{l֮]Kdd$lڴM6̠A5jFFz[Ydʖ-ɓsȈ3aܹ}̙9O?s%Or0`@@S,^pmj''' ̝.P(pww'<<\0`|Mc\$,,,Xxq@"K,dzzjmfgAl޼ 6hwuuJB033xe9,+SűMJ1CS88oF;Fm[v->'zߤ;FE6Oˑ(xm IDATGQ𶹸Xq <|JK#++c<{P  ›Ȕ)S2e ?=z۷o Xj1Z@˹sS:פI.]ġCr*3.S @>R{\u,W9t455չ‚vڱn::Zĉ'2d߿Ν;Yn^:Go?A(iz27/R69b5/[b:STLי]A<ɱ^}l97 \-%(v]Qn]Q>$MaÆ4kVf  !oXb׮]o߾߿@m+0/ځ+>>붧̂P*l߾Ǐs9._LBB+'ɨTR4+랖߽{7~^jҽ{w:wڵk M6u^A(j)i>i)J$R ^roGϚmޱ[D#i¸-s C*f6aB1Dӓy`Όiv6ի;|x{;ƍ´iHM…H5dҤź'AAm"OO<ƍԬYTϚ5k|r1c$""ggg077u>WE[laذaDGG#JI&oڌ *tދJet ???=֭[E0Q(6g{h@R2Ptl0~:U9sl,{>5*+}ӭ ;C;tfPc޿rnAT*q^M6y{mvvPJRMEA&۷]6]o?Sf̘F!,, gggmYqtttST$%%eς۶mɩ`Iu-Q*׏ɓ'ckkc͜9s򼿉I&p ݻu@vU6oɓ\2GUѣG_ymA(J?GFOFFo߾/]4 e"V^'Np̙Wh4={Pu5>{AxsvfXؙR#Hh?onuu{ Ǻl~sr^^]M4熝ȐQr8IhOم'L%{ @{|NuMgmY_T*պw&nh4NJL'SSSڵkǖ-[8q"͛7%/ZK.,XǏӸqΠFaѢE|'Ӏf̘ihٲe_FF}%44+WQ00+++`.^ݻ7+/^d|G,[7n Hݻ+ٰaCr9̟?_@Pv?ݐ3\dr)c[9hN7Ktf(P`MN1 =Bz(Y([ƦjUxr:i42GFZ՞$v?S1L8RR2#7{ˆ gٴ){RŬYGغ_,,X0{OPh4GǩXY:A(-w߅lԩPlY>S,Y¸q044wT*ٴicǎ%--+Ə]Vob>LܢL]670¨v-Y}cgX;:{W%vc]vcD{7X ؉`/"RH~@7nE=w 3s' x?ZZshqz;#Vd2JY3ڌ!3bdSQw,--{B٫Uǎ]wٳ>K^v}U-ǜH2#ծ];wvZ͛7y5Ѯ];F.:[[[|||Xj{S`A7n̄ U+1 r~G9?-Zv1eʔ4Ea5jDtt˗ggg,\{sX"{fĈy*+YeĈ4hCUs;lw +{w \6K'')8p-MY6cL~{X6ZLK8)OT)$XjJT$+/;B l-)cug-[N![rR2SGMK{}]-Ǯ(}SERsVLDZr;le7W;mRn"HQQL>o֦}ϵw3ra,XӇ m !I& !2%))`a͔)M07~e҅d}&3̄+< l˥Jz DaD̵8T%\b~j[o)rhI&FRgιR~1ʕ)ˣ<'2RًRJnI߾h̊a+2kfL3_yٸ|~Ѹa J7,?JJ!(( ,%-ky$c_WX[s(U$$FO'gLLR"4$mte֟|GKN%~_yKOF-TE,|s V;m _a6#0xOXػܲvM"R*UܾK3b^_LS En926TZ5%⟣R嗭ٳ-$L')HV%YDHH4&ߏkS@i-=zzzT]94~t|8 / !2&D!Dϟ?GƍI 3s?/JOb۬#mdCp6}Q) ZoKO1~cZÄ˹~}:6BocיEbdN!LB<@TqvL5?z̞ZZ|\bLҏ.6Ιn?9f!B!=LBB&D!,8Li|p/ |}UK]eӼ4}ju̶]B!f~; ^hڊ5ҥWg +}<|=qO>]vzeؿ{?@г tttJ=[wc/< Pܶ9&ľ}⋎&*2 ;tter SFMeƂkRMNJ&y0ajf-|񒄄 exB9"omFfe=#W 87WeU:_Itec?59RP =ݜ\B/^4ڣo`@bBOؼz[naI3KO;R̅3"#" |HJ_m;hblؽ'%%1fXκc&ϝ]!["#88<lTR/{Y2w Ph~&>>{?}L;ɖx=/r2B|N$(yĄM}V.3fm/: u7źh-7B!{&̜@m>a0uviLLL${nZh gҳLaq.:s) Ǵ1s|w.FPn <122pg!9JBG1c(\x_FTjX|YoafsB!늗,-X"M21e-Q굪P(|HXhsmll D.z\$$8J+Ced%woaieƼψyMi %EIF{6)T$#al]2KI$ܦqܻ}2ʨ?{:w>A!ܿ{Ws(1ϭkw>zz8qwnA@K޾c뗐IݹeAm/_ç$%&b_:Nꦞ^x$;]*[R}|]yxr yAnPct[w{*ReKRJE{) xH157USX r"y ^z7УxM޸}a;OB&"<^E"ԬW3stcOxde27n;59)+>WUkTk"dB! uJPNwJ!Ȍ%T ބbm۶n !!Aݷbܺ++|î;y\kUg{ 5#'Ǖq=4ٺ 7Jiө .Lw}GHJJ^-3s _s'1}܌ I/u^~2Cq>~8-]We V-}v?gigmkŖ}[(Z(;7e Nr5}7;߽+}'O>{y40gμ?иEcu[DxMKcU*%; o\ɐCXimyx![`:w B!>֩ccbQTX굪3m4l026}v6>WuBE QقqTx)EaPB) Y'jmkC bljC qxzo}|ofK||?uFj;šR9.]4*ŹvSsf`_؞y+QޱMξ=Aς}ty.yms21XXYPb B eV~:^0j@G_6άq%a=&U 5 48v%뚶j~'l\3Ϝ',4 S*U0JNmX8kGF's5f-ۦnW؎K^ެ7^BE gecng*315amYSRYt nC^ľ`л Y]B9dBSgiBfi+Q !yExX0#K7\ZkkUu{wxbLLM{_bf}ב6J{ټzF'T-1mz:D[[fnw=R+ !@9 !D6015!U‡;f@GOvCdcD9(E !E ӴiiiѮK;0Cu)sSFNaU.VƴӰpv]6wBeZ}ђؽeeڗEӺNJ-œҦm綜q=XgO3[AmVnYwѯc/Bx;`bb†=0|6uuzɗ{bfn+,\QFѮ^{J/>7ʂ}sg-]QܦV,<5Vת_ Z3g000ޝ{.Ze}?d䤑gʜIs(VG&iڪ)s'e-ޝѣ_wZkYR :t2rtttXq&[3wZoI睜8zeK _mCY?Jҵ(W<.RJE&ϝ̂ h[-*#*"GQXa~\'E!I& !D6(hYw%'&0B/uj*mzzXZ[RS ccr@ll,m:n(.㦍ءUJE{~'y0JcQkRJ*s,R ??v6\B䀀{%[}s2fnncnH4#ƏP)o[,@ĿU<vn"{m^eqI,-?tqʌgУ0/:>O֟ۡ!03Q!I8Jns; !B䰧r-B3HB D!&wO^v(9cR(^xn"B֣Uz~7.BȝBMM1v sevH&/UcݶB!+ 6&:505{*V.n.ڿBϗ$"5lڀWQQ3ZPmќÉiR9$BGԬW3KhB#D!f:ñ#[7lfDǑa}4=}] ZEV:cI!B!D.dB䀢%2sC!B!VRE!B!B)LB!B!Bd$B!j-yϏ~?΁B]$(B!Ȓ{1nxn^Ѿl2N=mXEeWxB!LB!BdIXh] yIxb~{6E&B"՜B!"$(NM:ۡ!"$(B!H۬Z+ޗQRp7~C:N 6gA,o`梙8TvPqI/Ӥe&͙1&&n] ]B!YLB!BhaX竡}QTwA]f7OYS -}BI\x#ҡ[uiEK۵CC0cc}MLB!8I& !B!Ԓn4J)Ɏ;00`e̝=<1qyW'??OToNOԉķ*Vv eIIIB!3Q!Bf/f%&$؟*N;NҴiik2Yn6E,B$B!jkկu} (;lK!y$B!j%ʔihB񄅆aam !";B!j+;`jnʮMP&+5>[pm#.&6A %%S!Dv& !B! ?m&̦o~t֖\;7^(@غn+ˎfB!aLB!BhU7 Yx5&܌a1l0ueѯ;'dԪ_ [{[004H3>ffgddi,J!B"gyL@CN=@͒r;!xG ǮoKr;/p<pۡ|PTJ%V"ELJr;!D&;B!z{Y!B!B!"$(B!B!I& !B!B!2EB!B!BLdB!B!"S$(B!B!I& !B!B!2EB!B!BLdB!Biզ~n-W/"ʺ{q%s'`Ss'RTYMEV,XۡpܽP" I& !B!D:LLM1260Ś%k;hFffRT"#W0n_ۡ!DB!B!GkR/?C!gFB!B4n\yobc)hY&-SX4q /B^`fnJ(X>SsqIchdHԬW-TQx{RzqE ZiƓ365 B_RRg\wZZ(Sfs9ͨZj}@||liܼZ[O>Z5#66ccffJm Y<؈ukPV51._Lll, 5h+>WZ*)))\t!$&&zccc7Wf5,-7NHIICRТ;Jc{s J5Ri}N?E"(P6;鎅eA*;p5DEFQ\=3QG(7ޗ1gR$&:uXc5&3ˡFbWȖSOSв !PhB/{(`Q!/P(`UDCx^4=zFv6kHHP;?ֶ֨T*^@l}+%˔Z$$$h{Xbgp|<XR%|ɝB!BK^m>m\}\9s4+cټp)]B*9yg'm;e߮lXvV6zW?Ct!~;9q WW*8VsRŨb]yތ:#nl\HM<޾?}̵lݿȈHa / 6&_9}4'.n*%;71~&Tdϩ+'}ln݈Tl7ddžL-O{N̘:;n~::5??髧8{ DV/JMq+|2<)_1s} D!B!ZDxV֖꣡, 0c 7SwLmZClX>kjf˿H"l۰tHII!2<Ss3LLfȨ!6v*U`#ȶ[ Sܾy'RZ%197䗟wTt2}9'[4VQn B~gmkCpnsꈭ-/3aaeʭ+ۨVlYEkRLI 4gUjTae~.]]]E3s3LLMsft]oҲ EKĿe^j;1|pu?Ǫ8~>-u(^w~~u;˰èߴ> N_vW8";,$u(C%uprY8T@32<BVZiV757\FFwBD!B!Zu(\0uץvZ8VuTLHHֵԪ_ k[{tuuiֺ)[l{ ]teݴێR^Mj֫wn<}DYfi6k:w@j2ޝ{ܻss> T߇:q+Y2hS(lߒkq5j2;"261&&:G:йAXZc0~##|$'%s-u.xl<cmcN$edbL؋g]8z.]iڪ)դv|K!r$B!j:vpIV?<9F dЈAļATacgow*FLP]݅vEL_0}-,{>DG`܌5 #Œ|[n|r|éU ۸.V@՚U~F#nieʧ.X7w޺a㛣gOKoiR泫>W߰~TQEHLHHP'CZxđPzjX]]Wd[<)V>~*gGS+.}]Z]}HblbG3 'hiiQ~c&.6{wXB]$(B!P˗/w/V,X@j3g(^8F ".6C%$(8fO;o5,mu+$I'WP&&™DGE:! &<|@bb",e:HA˂,[ȈHROҌO\l?.142Wqe:Rߎ{O*Rljљv x{y͖>W\ijկ+ѓT*™򽖙LI^x@? IDATNpd=/9)=[pq ---*>Z&"91T*FLf/Zjw :Pd1x8NKHP(WldӪMhkkJQ1}4L_QTcؚ+~fբ޺dbbb0zM1gN`llXnQɬzw^&~3.Q'lGL9}/ϝ[w ̔!$''1vX}ahdYX8kG]I!r"C"ȩYUn"H|"BdU<vn/137FtC< BC1gNuӭȈHN9A@tt)Q8Z7SWGf]`:-;hڲ KO3f||8::X&-9y$n؈eiԼ2037SW7NNJ ޺jVn> ds,O~:/_|o'wۏMꧻ2$("&&ԨWNiȾ#Pв :/w/֨_G^%?ccc7ϋ\Bհ$2"8VuĮ<^^iXG\|m--6 g䤑בֿ};&LrdQcjfJ)^C9(JRR8M%(]d%N/JReKҢ} wɾu%bhجaL ҂V_؈ ^PcH9:{~{m![nOhp(7$99 jckx2wBgI2QH2Q7I2Qd!LGc (n_uh߻c/&f5ip"d"3䘳B!Br=N;ERINm042,KPQQ!DdB!B2餤`Zj_1!$(B!ȓJ+Ew3 ?ţ{HJJp"B!$(B!ȓ0370{3WBߠ.B!B!B!D!B!B!D&I2Q!B!B)LB!B!Bd`B!B(p9ȩc'@rr2VC# So4 8(AU8rkhdHEiЬ>;9yc&_|Y-BBYd-m;FtgټtoكtMIIaظ#?;cuEKݱzU_;y.đ]KlLlc˪HYz~|'kY/Q*+2;gc&U}?x`$n{tW'>3Yhﵢ*pѢ/ }FIhg{N}3d?w o!&%/I6s57幧K.;䋿Ĕo'{t3]u?1jꉇH6COJRL~r I3Nm:ǟ)m;qcti5]7!=:|`,I2\wK_:k;+йCSb{}:ӕnwsއcN־{l5R^^^m~>6:ݷ%۷}'>^ms쟟Nm;[zmyx+GeeeԦsnen3ԦsD<=ta}/ŜuԶso=[w>1Wzs^sNzb.e__ngl#;2)KoFxq˝rcrVkcOJ˦M0'lrtk=۵>h6~~O_S?5۵.7_ws䢫/,}x4g9_{o *AR~\__Z k6䆫n[oL5\WTdsRQQQc˂ ${WLL:fV͓$#S?5w蘣N<*W[/NEUrǿ޻籇~3#˞ӲMm:nSmW_=vTwg|~!I6\鶿ib-2ڡU,`|ǹ $ޟ?:[.:3oMU_!,pկ3Ayaӎ3{o;1ruvC1,^$g쬌}D~~5]jlϘY!Ӵy$UW wD&:9}>,7]sSN< k^=%^zm.8ゼֻopvIM[oM[oZھ2'{r&}8yP>?-w9ϡs]i~w#2[}}ҰQ=ӳI9!WdϾ{dV[~9`̟ .+."W\pE$[ou%5>ꠌ\;qlr<:ќ{m{!c[ >jHPs?ݟv[7^V +r豇$y)1ON-ږ=kv^$)o¨Rg:9ouΟޑS=3!9bTμĕ?r\ 2򵚬:8gΟxvڽkF<|OnyxЩC&F.;+CϹtNcޤ&S9] Ix{<2~Rcv)7JEyE=8>;S)$.agV˄QK/^!g )LjhEj&IZo֦ƶo&k79Vk>J:-wunݺ١+Ι1ſ$g >#m6k_m߆S=5oV^ii٦ep"ڶ\Zj>ges/g)Ï?0I75M5MƍjsYWI^}UIf3q} 75mQ$IZ'ItM,Qo$UE|'2s57eÍ7 ゚ h?mѮ>Θd҄IѻGxL{ZN=8g~:3 /2rĨeéUy-7[vUb"Pú֩sxAL)+pU]6lT5.fI fUڭkvKw~Z臾ص[BSWdYp%YdI)}`l~~)׾~ٴ&ygsu_{Mϰۆ?ܑ[wkjSejeeoV}g]f͛eW[jevƛn{eiظQ5nTm7U.,Iҵ[׬twLz4InUs͆k,:w(Muuk뮽|@ kW&k/v[VySߛ_-=_K}W>ddilky׻ںY3g,=֭ߕOZzI+x_mK5\k5oKKM?6h<6|>djs9e1iݮU>8Oг/J;bV_μj򵗪 S>?!G:mU={^W^mz̙3N~so 3qٳolf, h<վejWv*Tݻ 2a=kviŹW$I)5]jk \KqSQ^=ժ )_ [ne6o]sSϛ_m ܫ6nݺf҄Gsߟ~ɒ%$ٶ5+h_VVV憫nLYYYvsdiԸQpŒ}DdIe޵Y\8.1xq``93o1TVVfEʒ%K_{^Y\q^?m~ؠE~og=3ٹ𬋖y#йCR^^^j}9crW;O-\민>?5,}nyt¤ՖOhT)re"RjeN3o~3OXs{p|^}{>'ԶliZi!s}E xӍzUWi]qys Gf֛ N߮Gi^<9<< 9c N<-o.ݺdݦf¨oWw4o߭ sO??lIGG!?#Fe'Iӧ޹{k}G>]ܙuO:fS{>kݹ떻-w-#'eЏK3h}31[[K.r墯ruvOs>COdȸGcb;g^z<3eq'I:){;..Z:v>sfE9 ֛N-ܐAhE?9'4_l*Nߝ(=*sg}٫z_7g7srv͛JSΛOY >KF,Ce&{$;mUmMf2~vK_TTT}3L͎X_羽"L\2̞5;#nc'^z4xP?{ھv5<ȓ0lv8|r)?r|^~'Mi=쑲IOe}3ڭK.4m4IU|ӵ[tR~k׬{-٧e挙H޻VgԫW/?\>8.S&O6oM0M)꫊tj?reף|>N&L6cUk*3srKW@77:`ZWP'Z,Z0 5JhV^U=;4gZ} ={Jݿk>z^zDآ_)HLX goI@!b"P"&@!b"P"&@!b"P"&@!b"P"&@!uVeWsW0fYS~5oU &9kk n/PMwYCDӆi[|J[aqezw{&@!b"P"&@!b"P"&@!b"P"&@!b"P"&@!b"P"&@!b"P"&@!b"P"&@!b"P"&@!b"P"&@!b"P"&@!b"P"&@!b"P"&@!b"P"&@!b"P"&@!b"P"&@!b"P"&@!b"P"&@!b"P"&@!b"P"&@!b"P"&@!b"P"&@!b"P"&@!;qIDATb"P"&@!b"P"&@!b"P"&@!b"P"&@!b"P"&@!b"P"&@!b"P"&@!b"P"&@!b"P"&@!b"P"&@!b"P"&@!b"P"&@!b"P"&@!b"P"&@!b"P"&@!b"P"&@!b"P"&@!b"P"&@!b"P"&@!b"P"&@!b"P"&@!b"P"&@!b"P"&@!b"P"&@!b"P"&@!b"P/%7"dIENDB`sslh-2.1.4/doc/sslh-examples-v3.svg000066400000000000000000004736311463704647400171250ustar00rootroot00000000000000 Viewer does not support full SVG 1.1 Families Prerouting Hook SYN-ACK-connection SYN-connection SYN-connection Prerouting Hook SYN-connection SYN-connection SYN-connection Application Layer Server Prerouting Hook dummy0D0_IP Prerouting Hook eth0S0_IP Prerouting Hook sslh Prerouting Hook sshd Prerouting Hook internet Prerouting Hook ClientC_IP Prerouting Hook routingtable Application Layer Server1/Router Prerouting Hook eth0S0_IP Prerouting Hook sslh Prerouting Hook internet Prerouting Hook ClientC_IP Prerouting Hook routingtable Prerouting Hook eth1S1_IP Application Layer Server2 Prerouting Hook eth0S0_IPS1_IP Prerouting Hook sshd Application Layer Additional S1_IP configuredonly for sshd!Server1 is Default Gatewayback to the internet,no additional configurationneeded Prerouting Hook internet Prerouting Hook ClientC_IP Application Layer Server1/Router Prerouting Hook eth0S0_IP Prerouting Hook sslh Prerouting Hook routingtable Prerouting Hook eth1S1_IP Application Layer Server2Router Prerouting Hook sshd Prerouting Hook routingtable Prerouting Hook eth1S1_IP Prerouting Hook internet Prerouting Hook eth0S0_IPS1_IP Application Layer Default Gateway points toeth1so special routing rule mustroute S1_IP back to Server1 Prerouting Hook connection withoutrouting deflection SYN-connection SYN-connection Application Layer Scenario 1 Scenario1 Scenario 2 Scenario 3 Prerouting Hook Version 3 2024-06-07 SYN-connection SYN-connection Application Layer Scenario 2 Scenario1 Scenario 2 Scenario 3 Application Layer Scenario 3 Scenario1 Scenario 2 Scenario 3 sslh-2.1.4/doc/tproxy.md000066400000000000000000000363711463704647400151540ustar00rootroot00000000000000# Transparent proxy using packet marking Before reading further, make sure you try to set up transparent proxying using [IP routing](doc/simple_transparent_proxy.md). It is conceptually easier to understand, cleaner, and more portable. Using this method is very tricky and detail-dependant: depending on whether the target server and sslh are on the same machine, different machines, or different dockers, and tool versions, all seem to change the required network configuration somewhat. If it doesn't work, it's almost certain that the problem is not linked to `sslh` but to the network setup that surrounds it. If in trouble, it might be worth trying to set up the network rules with a simpler server than `sslh`, such as [`socat`](http://www.dest-unreach.org/socat/) Users have tried to do at least the following: * `sslh` and the target servers run on the same host (see [below](#transparent-proxy-to-one-host)) * `sslh` runs on a host, and the target servers run on LXC or dockers running on the same host. No known working setup. * `sslh` runs on a host, and the target servers run on different hosts on the same local network(see [below](#transparent-proxy-to-two-hosts)) * `sslh` runs on a host, and the target servers run on a different host on a different network (there is a [usecase](https://github.com/yrutschle/sslh/issues/295) for this). No known working setup, and it's unclear it is possible. ## Transparent proxy to one host ### Linux `sslh` needs extended rights to perform this: you'll need to give it `CAP_NET_RAW` capabilities (see appropriate chapter) or run it as root (but don't do that). The firewalling tables also need to be adjusted as follows. I don't think it is possible to have `httpd` and `sslh` both listen to 443 in this scheme -- let me know if you manage that: # Set route_localnet = 1 on all interfaces so that ssl can use "localhost" as destination sysctl -w net.ipv4.conf.default.route_localnet=1 sysctl -w net.ipv4.conf.all.route_localnet=1 # DROP martian packets as they would have been if route_localnet was zero # Note: packets not leaving the server aren't affected by this, thus sslh will still work iptables -t raw -A PREROUTING ! -i lo -d 127.0.0.0/8 -j DROP iptables -t mangle -A POSTROUTING ! -o lo -s 127.0.0.0/8 -j DROP # Mark all connections made by ssl for special treatment (here sslh is run as user "sslh") iptables -t nat -A OUTPUT -m owner --uid-owner sslh -p tcp --tcp-flags FIN,SYN,RST,ACK SYN -j CONNMARK --set-xmark 0x01/0x0f # Outgoing packets that should go to sslh instead have to be rerouted, so mark them accordingly (copying over the connection mark) iptables -t mangle -A OUTPUT ! -o lo -p tcp -m connmark --mark 0x01/0x0f -j CONNMARK --restore-mark --mask 0x0f # Configure routing for those marked packets ip rule add fwmark 0x1 lookup 100 ip route add local 0.0.0.0/0 dev lo table 100 Transparent proxying with IPv6 is similarly set up as follows: # Set route_localnet = 1 on all interfaces so that ssl can use "localhost" as destination # Not sure if this is needed for ipv6 though sysctl -w net.ipv4.conf.default.route_localnet=1 sysctl -w net.ipv4.conf.all.route_localnet=1 # DROP martian packets as they would have been if route_localnet was zero # Note: packets not leaving the server aren't affected by this, thus sslh will still work ip6tables -t raw -A PREROUTING ! -i lo -d ::1/128 -j DROP ip6tables -t mangle -A POSTROUTING ! -o lo -s ::1/128 -j DROP # Mark all connections made by ssl for special treatment (here sslh is run as user "sslh") ip6tables -t nat -A OUTPUT -m owner --uid-owner sslh -p tcp --tcp-flags FIN,SYN,RST,ACK SYN -j CONNMARK --set-xmark 0x01/0x0f # Outgoing packets that should go to sslh instead have to be rerouted, so mark them accordingly (copying over the connection mark) ip6tables -t mangle -A OUTPUT ! -o lo -p tcp -m connmark --mark 0x01/0x0f -j CONNMARK --restore-mark --mask 0x0f # Configure routing for those marked packets ip -6 rule add fwmark 0x1 lookup 100 ip -6 route add local ::/0 dev lo table 100 Explanation: To be able to use `localhost` as destination in your sslh config along with transparent proxying you have to allow routing of loopback addresses as done above. This is something you usually should not do (see [this stackoverflow post](https://serverfault.com/questions/656279/how-to-force-linux-to-accept-packet-with-loopback-ip/656484#656484)) The two `DROP` iptables rules emulate the behaviour of `route_localnet` set to off (with one small difference: allowing the reroute-check to happen after the fwmark is set on packets destined for sslh). See [this diagram](https://upload.wikimedia.org/wikipedia/commons/3/37/Netfilter-packet-flow.svg) for a good visualisation showing how packets will traverse the iptables chains. Note: You have to run `sslh` as dedicated user (in this example the user is also named `sslh`), to not mess up with your normal networking. These rules will allow you to connect directly to ssh on port 22 (or to any other service behind sslh) as well as through sslh on port 443. Also remember that iptables configuration and ip routes and rules won't be necessarily persisted after you reboot. Make sure to save them properly. For example in CentOS7, you would do `iptables-save > /etc/sysconfig/iptables`, and add both `ip` commands to your `/etc/rc.local`. ### FreeBSD Given you have no firewall defined yet, you can use the following configuration to have ipfw properly redirect traffic back to sslh /etc/rc.conf firewall_enable="YES" firewall_type="open" firewall_logif="YES" firewall_coscripts="/etc/ipfw/sslh.rules" /etc/ipfw/sslh.rules #! /bin/sh # ssl ipfw add 20000 fwd 192.0.2.1,443 log tcp from 192.0.2.1 8443 to any out ipfw add 20010 fwd 2001:db8::1,443 log tcp from 2001:db8::1 8443 to any out # ssh ipfw add 20100 fwd 192.0.2.1,443 log tcp from 192.0.2.1 8022 to any out ipfw add 20110 fwd 2001:db8::1,443 log tcp from 2001:db8::1 8022 to any out # xmpp ipfw add 20200 fwd 192.0.2.1,443 log tcp from 192.0.2.1 5222 to any out ipfw add 20210 fwd 2001:db8::1,443 log tcp from 2001:db8::1 5222 to any out # openvpn (running on other internal system) ipfw add 20300 fwd 192.0.2.1,443 log tcp from 198.51.100.7 1194 to any out ipfw add 20310 fwd 2001:db8::1,443 log tcp from 2001:db8:1::7 1194 to any out General notes: This will only work if `sslh` does not use any loopback addresses (no `127.0.0.1` or `localhost`), you'll need to use explicit IP addresses (or names): sslh --listen 192.168.0.1:443 --ssh 192.168.0.1:22 --tls 192.168.0.1:4443 This will not work: sslh --listen 192.168.0.1:443 --ssh 127.0.0.1:22 --tls 127.0.0.1:4443 Transparent proxying means the target server sees the real origin address, so it means if the client connects using IPv6, the server must also support IPv6. It is easy to support both IPv4 and IPv6 by configuring the server accordingly, and setting `sslh` to connect to a name that resolves to both IPv4 and IPv6, e.g.: sslh --transparent --listen :443 --ssh insideaddr:22 /etc/hosts: 192.168.0.1 insideaddr 201::::2 insideaddr Upon incoming IPv6 connection, `sslh` will first try to connect to the IPv4 address (which will fail), then connect to the IPv6 address. ## Transparent Proxy to Two Hosts Tutorial by Sean Warner. 19 June 2019 20:35 ### Aim * Show that `sslh` can transparently proxy requests from the internet to services on two separate hosts that are both on the same LAN. * The IP address of the client initiating the request is what the destination should see… and not the IP address of the host that `sslh` is running on, which is what happens when `sslh` is not running in transparent mode. * The solution here only works for my very specific use-case but hopefully others can adapt it to suits their needs. ### Overview of my Network Two Raspberry Pis on my home LAN: * Pi A: 192.168.1.124 – `sslh` (Port 4433), Apache2 web server for https (port 443), `stunnel` (port 4480) to decrypt ssh traffic and forward to SSH server (also on Pi A at Port 1022) * Pi B: 192.168.1.123 - HTTP server (port 8000), SSH server (port 1022 on PiB). * I send traffic from the internet to my router's external port 443 then use a port forward rule in my router to map that to internal port 4433 where sslh is listening. ![Architecture](tproxy.svg) ### `sslh` build   `sslh` Version: sslh v1.19c-2-gf451cc8-dirty. I compiled sslh from sources giving the binary pretty much all possible options such as Posix capabilities and systemd support.. here are the first few lines of the makefile:   ``` # Configuration VERSION=$(shell ./genver.sh -r) ENABLE_REGEX=1         # Enable regex probes USELIBCONFIG=1         # Use libconfig? (necessary to use configuration files) USELIBPCRE=1 # Use libpcre? (needed for regex on musl) USELIBWRAP=1         # Use libwrap? USELIBCAP=1         # Use libcap? USESYSTEMD=1         # Make use of systemd socket activation COV_TEST=         # Perform test coverage? PREFIX=/usr/local BINDIR=$(PREFIX)/sbin MANDIR=$(PREFIX)/share/man/man8 MAN=sslh.8.gz         # man page name # End of configuration -- the rest should take care of # itself ```   ### systemd setup Create an sslh systemd service file... ``` # nano /lib/systemd/system/sslh.service ``` Paste in this contents… ``` [Unit] Description=SSL/SSH multiplexer After=network.target Documentation=man:sslh(8) [Service] #EnvironmentFile=/etc/default/sslh #ExecStart=/usr/local/sbin/sslh $DAEMON_OPTS ExecStart=/usr/local/sbin/sslh -F /etc/sslh/sslh.cfg KillMode=process [Install] WantedBy=multi-user.target ``` Save it and then… ``` # systemctl daemon-reload ``` Start it again to test… ``` # systemctl start sslh ```   ### Configure `sslh` First stop `sslh` then open the config file and replace with below, save and start `sslh` again ``` # systemctl stop sslh # nano /etc/sslh/sslh.cfg # systemctl start sslh ``` ``` verbose: true; foreground: true; inetd: false; numeric: true; transparent: true; timeout: 2; user: "sslh"; pidfile: "/var/run/sslh.pid"; chroot: "/var/empty"; # You must have a port forward rule in the router: external port 443 <-> internal port 4433 # Local ip address of PiA is: 192.168.1.124, sslh and stunnel4 are running on this Pi # Local ip address of PiB is: 192.168.1.123, http server and ssh server on this Pi listen: ( { host: "192.168.1.124"; port: "4433"; } ); # sslh demultiplexes based on the Protocol and Hostname protocols: ( { name: "tls"; sni_hostnames: [ "www.example.com" ]; host: "192.168.1.124"; port: "443"; log_level: 1; }, # This probe is for tls encrypted ssh. SSLH forwards it to stunnel on port 4480 which decrypts it and sends it to the ssh server on PiA port 1022 { name: "tls"; sni_hostnames: [ "ssh.example.com" ]; host: "192.168.1.124"; port: "4480"; log_level: 1; }, { name: "http"; host: "192.168.1.123"; port: "8000"; log_level: 1; }, { name: "ssh"; host: "192.168.1.123"; port: "1022"; log_level: 1; } ); ```   ### Configure `stunnel` First stop `stunnel` then open the config file and replace with below, save and start `stunnel` again ``` # systemctl stop stunnel4 # nano /etc/stunnel/stunnel.conf # systemctl start stunnel4 ``` ``` # Debugging stuff (may be useful for troubleshooting) foreground = yes #debug = 5 # this is the default debug = 7 output = /var/log/stunnel4/stunnel.log pid = /var/run/stunnel4/stunnel.pid fips = no cert = /etc/letsencrypt/live/example.com/fullchain.pem key = /etc/letsencrypt/live/example.com/privkey.pem [ssh] accept = 192.168.1.124:4480 connect = 192.168.1.124:1022 TIMEOUTclose = 0 ```   ### Configure iptables for Pi A The `_add.sh` script creates the rules, the `_rm.sh` script removes the rules. They will be lost if you reboot but there are ways to make them load again on start-up.. ``` # nano /usr/local/sbin/piA_tproxy_add.sh ``` ``` piA_tproxy_add.sh iptables -t mangle -N SSLH iptables -t mangle -A PREROUTING -p tcp -m socket --transparent -j SSLH iptables -t mangle -A OUTPUT --protocol tcp --out-interface eth0 -m multiport --sport 443,4480 --jump SSLH iptables -t mangle -A SSLH --jump MARK --set-mark 0x1 iptables -t mangle -A SSLH --jump ACCEPT ip rule add fwmark 0x1 lookup 100 ip route add local 0.0.0.0/0 dev lo table 100 ``` ``` # nano /usr/local/sbin/piA_tproxy_rm.sh ``` ``` piA_tproxy_rm.sh iptables -t mangle -D PREROUTING -p tcp -m socket --transparent -j SSLH iptables -t mangle -D OUTPUT --protocol tcp --out-interface eth0 -m multiport --sport 443,4480 --jump SSLH iptables -t mangle -D SSLH --jump MARK --set-mark 0x1 iptables -t mangle -D SSLH --jump ACCEPT iptables -t mangle -X SSLH ip rule del fwmark 0x1 lookup 100 ip route del local 0.0.0.0/0 dev lo table 100 ``` Make them executable.. ``` # chmod +rx piA_tproxy_add.sh # chmod +rx piA_tproxy_rm.sh ``` Now run the "add" script on Pi A! ``` # piA_tproxy_add.sh # piA_tproxy_rm.sh ``` ## Configure iptables for Pi B ``` # nano /usr/local/sbin/piB_tproxy_add.sh ``` ``` piB_tproxy_add.sh iptables -t mangle -N SSLHSSL iptables -t mangle -A OUTPUT -o eth0 -p tcp -m multiport --sport 1022,8000 -j SSLHSSL iptables -t mangle -A SSLHSSL --jump MARK --set-mark 0x1 iptables -t mangle -A SSLHSSL --jump ACCEPT ip rule add fwmark 0x1 lookup 100 ip route add default via 192.168.1.124 table 100 ip route flush cache ``` ``` # nano /usr/local/sbin/piB_tproxy_rm.sh ``` ``` iptables -t mangle -D OUTPUT -o eth0 -p tcp -m multiport --sport 1022,8000 -j SSLHSSL iptables -t mangle -D SSLHSSL --jump MARK --set-mark 0x1 iptables -t mangle -D SSLHSSL --jump ACCEPT iptables -t mangle -X SSLHSSL ip rule del fwmark 0x1 lookup 100 ip route del default via 192.168.1.124 table 100 ip route flush cache ``` Make them executable.. ``` # chmod +rx piB_tproxy_add.sh # chmod +rx piB_tproxy_rm.sh ``` Now run the "add" script on Pi B! ``` # piB_tproxy_add.sh # piB_tproxy_rm.sh ```   ### Testing * Getting to sshd on PiA I did this test using 4G from my phone (outside the LAN) To simulate this I use `proxytunnel`. External port 443 is forwarded by my router to 4433. I need to arrive at `sslh` (port 4433) with ssh encrypted as TLS (hence I use the -e switch) and the `sni_hostname` set to ssh.example.com so that `sslh` will demultiplex to `stunnel` (port 4480) which will decrypt and forward to ssh server on PiA… see `sslh.cfg` and `stunnel.conf`. The first IP:port is just a free HTTPS proxy I found on https://free-proxy-list.net I execute this command from a terminal window.. ``` # proxytunnel -v -e -C root.pem -p 78.141.192.198:8080 -d ssh.example.com:443 ``` * Getting to sshd on PiB I did this test using 4G from my phone (outside the LAN) My smartphone telecom provider blocks ssh over port 443 so I need to use `proxytunnel` to encrypt. Use the Proxytunnel `-X` switch to encrypt from local proxy to destination only so by the time we get to the destination it is unencrypted and `sslh` will see the ssh protocol and demultiplex to PiB as per `sslh.cfg`. ``` # proxytunnel -v -X -C root.pem -p 78.141.192.198:8080 -d ssh.example.com:443 ``` Now when you test it all look at the output in daemon.log like this: ``` # grep -i 'ssl' /var/log/daemon.log ``` You should see that the IP address and port from the “connection from” and “forwarded from” fields are the same. Special thanks and appreciation to Michael Yelsukov without whom I would never have got this working. Any feedback or corrections very welcome! sslh-2.1.4/doc/tproxy.svg000066400000000000000000000775221463704647400153560ustar00rootroot00000000000000 image/svg+xml Client Internet Router Local Area Network 443 443 443 4433 4433 SSLH 192.168.1.124 (Pi A) 192.168.1.123 (Pi B) Web Server SSH Server http 8000 8000 ssh 1022 1022 Web Server STUNNEL SSH Server 443 443 tls 4480 tls 1022 ssh sslh-2.1.4/echo_test.cfg000066400000000000000000000002431463704647400151430ustar00rootroot00000000000000 # TODO: c2s does not warn if udp: 1 (instead of 'true') udp: true; prefix: "hello"; listen: "localhost:9000"; listen-host: "localhost"; listen-port: "9000"; sslh-2.1.4/echosrv-conf.c000066400000000000000000001151561463704647400152570ustar00rootroot00000000000000/* Generated by conf2struct (https://www.rutschle.net/tech/conf2struct/README) * on Sat Apr 30 09:55:03 2022. # conf2struct: generate libconf parsers that read to structs # Copyright (C) 2018-2021 Yves Rutschle # All rights reserved. # # Redistribution and use in source and binary forms, with or without # modification, are permitted provided that the following conditions are met: # # 1. Redistributions of source code must retain the above copyright notice, # this list of conditions and the following disclaimer. # 2. Redistributions in binary form must reproduce the above copyright # notice, this list of conditions and the following disclaimer in the # documentation and/or other materials provided with the distribution. # # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" # AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE # IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE # ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE # LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR # CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF # SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS # INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN # CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) # ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE # POSSIBILITY OF SUCH DAMAGE. */ #define _GNU_SOURCE #include #ifdef LIBCONFIG # include #endif #include #include #include #include "echosrv-conf.h" #include "argtable3.h" #ifdef LIBPCRE #define PCRE2_CODE_UNIT_WIDTH 8 #include typedef struct { PCRE2_SIZE rm_so; PCRE2_SIZE rm_eo; } regmatch_t; #else #include #endif /* This gets included in the output .c file */ /* Libconfig 1.4.9 is still used by major distributions * (e.g. CentOS7) and had a different name for * config_setting_lookup */ #if LIBCONFIG_VER_MAJOR == 1 #if LIBCONFIG_VER_MINOR == 4 #if LIBCONFIG_VER_REVISION == 9 #define config_setting_lookup config_lookup_from #endif #endif #endif /* config_type, lookup_fns, type2str are related, keep them together */ typedef enum { CFG_BOOL, CFG_INT, CFG_INT64, CFG_FLOAT, CFG_STRING, CFG_GROUP, CFG_ARRAY, CFG_LIST, } config_type; /* /config_type */ const char* type2str[] = { "boolean", "int", "int64", "float", "string", "group", "array", "list", }; typedef union { int def_bool; int def_int; long long def_int64; double def_float; char* def_string; } any_val; struct config_desc { const char* name; int type; struct config_desc * sub_group; /* Table for compound types (list and group) */ void* arg_cl; /* command-line argument for this setting */ void* base_addr; /* Base of the structure (filled at runtime). Probably not useable for list elements */ size_t offset; /* Offset of setting in the structure */ size_t offset_len; /* Offset of *_len field, for arrays and lists */ size_t offset_present; /* offset of *_is_present field, for optional settings */ size_t size; /* Size of element, or size of group for groups and lists */ int array_type; /* type of array elements, when type == CFG_ARRAY */ int mandatory; int optional; any_val default_val; }; #ifndef LIBCONFIG /* Stubs in case you don't want libconfig */ typedef void config_setting_t; typedef int config_t; #define CONFIG_TRUE 1 #define CONFIG_FALSE 0 #define make_config_setting_lookup(type) \ int config_setting_lookup_##type(const config_setting_t* a, const char* b, void* c) { \ return 0; \ } #define make_config_setting_get(type, ret_type) \ ret_type config_setting_get_##type(const config_setting_t* a) { \ return 0; \ } enum { CONFIG_TYPE_INT, CONFIG_TYPE_BOOL, CONFIG_TYPE_INT64, CONFIG_TYPE_FLOAT, CONFIG_TYPE_STRING }; make_config_setting_lookup(bool); make_config_setting_lookup(int); make_config_setting_lookup(int64); make_config_setting_lookup(float); make_config_setting_lookup(string); make_config_setting_get(bool, int); make_config_setting_get(int, int); make_config_setting_get(int64, long long int); make_config_setting_get(float, double); make_config_setting_get(string, char*); config_setting_t* config_root_setting(config_t* c) { return NULL; } config_setting_t* config_lookup(config_t* c, const char* b) { return NULL; } void config_init(config_t* c) { return; } char* config_setting_name(config_setting_t* c) { return NULL; } int config_setting_is_list(config_setting_t* c) { return 0; } int config_setting_is_array(config_setting_t* c) { return 0; } int config_setting_is_scalar(config_setting_t* c) { return 0; } int config_setting_index(const config_setting_t *setting) { return 0; } config_setting_t* config_setting_lookup(config_setting_t* a, char* b) { return NULL; } int config_setting_remove(config_setting_t* cfg, char* name) { return 0; } int config_setting_type(config_setting_t* s) { return -1; } int config_setting_length(config_setting_t* a) { return 0; } config_setting_t* config_setting_get_elem(config_setting_t* a, int i) { return NULL; } int config_read_file(config_t* a, const char* b) { return CONFIG_TRUE; } int config_error_line(config_t* c) { return 0; } char* config_error_text(config_t* c) { return NULL; } #endif /* This is the same as config_setting_lookup_string() except it allocates a new string which belongs to the caller */ static int myconfig_setting_lookup_stringcpy( const config_setting_t* setting, const char* name, char** value) { const char* str; *value = NULL; if (config_setting_lookup_string(setting, name, &str) == CONFIG_TRUE) { asprintf(value, "%s", str); return CONFIG_TRUE; } else { return CONFIG_FALSE; } } typedef int (*lookup_fn)(const config_setting_t*, const char*, void*); lookup_fn lookup_fns[] = { (lookup_fn)config_setting_lookup_bool, (lookup_fn)config_setting_lookup_int, (lookup_fn)config_setting_lookup_int64, (lookup_fn)config_setting_lookup_float, (lookup_fn)myconfig_setting_lookup_stringcpy, NULL, /* CFG_GROUP */ NULL, /* CFG_ARRAY */ NULL, /* CFG_LIST */ }; /* Copy an any_val to arbitrary memory location */ /* 0: success * <0: error */ static int any_valcpy(config_type type, void* target, any_val val) { switch(type) { case CFG_BOOL: *(int*)target = val.def_bool; break; case CFG_INT: *(int*)target = val.def_int; break; case CFG_INT64: *(long long*)target = val.def_int64; break; case CFG_FLOAT: *(double*)target = val.def_float; break; case CFG_STRING: *(char**)target = val.def_string; break; default: fprintf(stderr, "Unknown type specification %d\n", type); return -1; } return 1; } /* Copy the value of a setting to an arbitrary memory that * must be large enough */ /* 0: success * <0: error */ static int settingcpy(config_type type, void* target, const config_setting_t* setting) { any_val val; char* str; switch(type) { case CFG_BOOL: val.def_bool = config_setting_get_bool(setting); *(int*)target = val.def_bool; break; case CFG_INT: val.def_int = config_setting_get_int(setting); *(int*)target = val.def_int; break; case CFG_INT64: val.def_int64 = config_setting_get_int64(setting); *(long long*)target = val.def_int64; break; case CFG_FLOAT: val.def_float = config_setting_get_float(setting); *(double*)target = val.def_int64; break; case CFG_STRING: asprintf(&str, "%s", config_setting_get_string(setting)); val.def_string = str; *(char**)target = val.def_string; break; default: fprintf(stderr, "Unknown type specification %d\n", type); return -1; } return 0; } /* Copy the value of a command line arg to arbitrary memory * that must be large enough for the type */ /* 0: success * <0: error */ static int clcpy(config_type type, void* target, const void* cl_arg) { any_val val; char* str; switch(type) { case CFG_BOOL: val.def_bool = (*(struct arg_lit**)cl_arg)->count; *(int*)target = val.def_bool; break; case CFG_INT: val.def_int = (*(struct arg_int**)cl_arg)->ival[0]; *(int*)target = val.def_int; break; case CFG_INT64: val.def_int64 = (*(struct arg_int**)cl_arg)->ival[0]; *(long long*)target = val.def_int64; break; case CFG_FLOAT: val.def_float = (*(struct arg_dbl**)cl_arg)->dval[0]; *(double*)target = val.def_float; break; case CFG_STRING: asprintf(&str, "%s", (*(struct arg_str**)cl_arg)->sval[0]); val.def_string = str; *(char**)target = val.def_string; break; default: fprintf(stderr, "Unknown type specification %d\n", type); return -1; } return 0; } /* Copy the value of a string argument to arbitrary memory * location that must be large enough, converting on the way * (i.e. CFG_INT gets atoi() and so on) */ /* 0: success * <0: error */ static int stringcpy(config_type type, void* target, char* from) { any_val val; switch(type) { case CFG_BOOL: val.def_bool = (*from != '0'); *(int*)target = val.def_bool; break; case CFG_INT: val.def_int = strtol(from, NULL, 10); *(int*)target = val.def_int; break; case CFG_INT64: val.def_int64 = strtoll(from, NULL, 10); *(long long*)target = val.def_int64; break; case CFG_FLOAT: val.def_float = strtod(from, NULL); *(double*)target = val.def_float; break; case CFG_STRING: val.def_string = from; *(char**)target = val.def_string; break; default: fprintf(stderr, "Unknown type specification %d\n", type); return -1; } return 0; } /* Element to describe the target of a compound element * element: which config entry is being changed * match: if >0, index in pmatch to set * if 0, don't match but init with value * value: constant if not matching */ struct compound_cl_target { struct config_desc * element; int match; any_val value; }; /* Element to describe one compound command line argument * An argument is string that gets matched against a regex, * then match-groups get evaluated to each targets[]. * For lists, base_entry points to the config_setting so we * can append to it */ struct compound_cl_arg { const char* regex; struct arg_str** arg_cl; /* arg_str entry for this compound option */ struct config_desc * base_entry; struct compound_cl_target* targets; /* If override_desc is set, it points to the descriptor of the element in the group which will be checked for override. Then, override_matchindex indicates the command-line parameter match used to compare against override_desc to know if this group is overridden. If override_matchindex is 0, we don't match from the command-line but from a constant stored in override_const instead */ struct config_desc * override_desc; int override_matchindex; char* override_const; }; struct arg_file* echocfg_conffile; struct arg_lit* echocfg_udp; struct arg_str* echocfg_prefix; struct arg_str* echocfg_listen_host; struct arg_str* echocfg_listen_port; struct arg_str* echocfg_listen; struct arg_end* echocfg_end; static struct config_desc table_echocfg_listen[] = { { /* name */ "host", /* type */ CFG_STRING, /* sub_group*/ NULL, /* arg_cl */ & echocfg_listen_host, /* base_addr */ NULL, /* offset */ offsetof(struct echocfg_listen_item, host), /* offset_len */ 0, /* offset_present */ 0, /* size */ sizeof(char*), /* array_type */ -1, /* mandatory */ 1, /* optional */ 0, /* default_val*/ .default_val.def_string = NULL }, { /* name */ "port", /* type */ CFG_STRING, /* sub_group*/ NULL, /* arg_cl */ & echocfg_listen_port, /* base_addr */ NULL, /* offset */ offsetof(struct echocfg_listen_item, port), /* offset_len */ 0, /* offset_present */ 0, /* size */ sizeof(char*), /* array_type */ -1, /* mandatory */ 1, /* optional */ 0, /* default_val*/ .default_val.def_string = NULL }, { 0 } }; static struct config_desc table_echocfg[] = { { /* name */ "udp", /* type */ CFG_BOOL, /* sub_group*/ NULL, /* arg_cl */ & echocfg_udp, /* base_addr */ NULL, /* offset */ offsetof(struct echocfg_item, udp), /* offset_len */ 0, /* offset_present */ 0, /* size */ sizeof(int), /* array_type */ -1, /* mandatory */ 0, /* optional */ 0, /* default_val*/ .default_val.def_bool = 0 }, { /* name */ "prefix", /* type */ CFG_STRING, /* sub_group*/ NULL, /* arg_cl */ & echocfg_prefix, /* base_addr */ NULL, /* offset */ offsetof(struct echocfg_item, prefix), /* offset_len */ 0, /* offset_present */ 0, /* size */ sizeof(char*), /* array_type */ -1, /* mandatory */ 1, /* optional */ 0, /* default_val*/ .default_val.def_string = NULL }, { /* name */ "listen", /* type */ CFG_LIST, /* sub_group*/ table_echocfg_listen, /* arg_cl */ NULL, /* base_addr */ NULL, /* offset */ offsetof(struct echocfg_item, listen), /* offset_len */ offsetof(struct echocfg_item, listen_len), /* offset_present */ 0, /* size */ sizeof(struct echocfg_listen_item), /* array_type */ -1, /* mandatory */ 1, /* optional */ 0, /* default_val*/ .default_val.def_int = 0 }, { 0 } }; static struct compound_cl_target echocfg_listen_targets [] = { { & table_echocfg_listen[0], 1, .value.def_string = "0" }, { & table_echocfg_listen[1], 2, .value.def_string = "0" }, { 0 } }; static struct compound_cl_arg compound_cl_args[] = { { /* arg: listen */ .regex = "(.+):(\\w+)", .arg_cl = & echocfg_listen, .base_entry = & table_echocfg [2], .targets = echocfg_listen_targets, .override_desc = NULL, .override_matchindex = 0, .override_const = NULL, }, { 0 } }; /* Enable debug to follow the parsing of tables */ #if 0 #define TRACE_READ(x) printf x #define TRACE_READ_PRINT_SETTING 1 #else #define TRACE_READ(x) #define TRACE_READ_PRINT_SETTING 0 #endif /* Enable debug to follow the parsing of compound options */ #if 0 #define TRACE_CMPD(x) printf x #define TRACE_CMPD_PRINT_SETTING 1 #else #define TRACE_CMPD(x) #define TRACE_CMPD_PRINT_SETTING 0 #endif static void print_setting(config_type type, void* val) { if (TRACE_READ_PRINT_SETTING || TRACE_CMPD_PRINT_SETTING) { switch(type) { case CFG_BOOL: case CFG_INT: printf("%d", *(int*)val); break; case CFG_INT64: printf("%lld", *(long long*)val); break; case CFG_FLOAT: printf("%f", *(double*)val); break; case CFG_STRING: printf("`%s'", *(char**)val); break; case CFG_GROUP: case CFG_LIST: case CFG_ARRAY: break; } } } /* Changes all dashes to underscores in a string of * vice-versa */ static void strswap_ud(const char target, char* str) { char* c; for (c = str; *c; c++) if (*c == (target == '_' ? '-' : '_')) *c = (target == '_' ? '_' : '-'); } /* Same as config_setting_lookup() but looks up with dash or * underscore so `my_setting` and `my-setting` match the same */ static config_setting_t* config_setting_lookup_ud(config_setting_t* cfg, struct config_desc* desc) { config_setting_t* setting; char name[strlen(desc->name)+1];; strcpy(name, desc->name); strswap_ud('_', name); setting = config_setting_lookup(cfg, name); if (setting) return setting; strswap_ud('-', name); setting = config_setting_lookup(cfg, name); return setting; } static int lookup_typed_ud(config_setting_t* cfg, void* target, struct config_desc *desc) { lookup_fn lookup_fn = lookup_fns[desc->type]; char name[strlen(desc->name)+1];; strcpy(name, desc->name); strswap_ud('_', name); if (lookup_fn(cfg, name, ((char*)target) + desc->offset) == CONFIG_TRUE) return CONFIG_TRUE; strswap_ud('-', name); return lookup_fn(cfg, name, ((char*)target) + desc->offset); } /* Removes a setting, trying both underscores and dashes as * name (so deleting 'my-setting' deletes both 'my_setting' * and 'my-setting') */ static int setting_delete_ud(config_setting_t* cfg, struct config_desc *desc) { char name[strlen(desc->name)+1];; strcpy(name, desc->name); strswap_ud('_', name); if (config_setting_remove(cfg, name) == CONFIG_TRUE) return CONFIG_TRUE; strswap_ud('-', name); return config_setting_remove(cfg, name); } /* When traversing configuration, allocate memory for plural * types, init for scalars */ static void read_block_init(void* target, config_setting_t* cfg, struct config_desc* desc) { size_t len = 0; void* block; config_setting_t* setting; switch (desc->type) { case CFG_LIST: case CFG_ARRAY: if (cfg) { setting = config_setting_lookup_ud(cfg, desc); if (setting) len = config_setting_length(setting); } block = calloc(len, desc->size); *(size_t*)(((char*)target) + desc->offset_len) = len; *(void**)(((char*)target) + desc->offset) = block; TRACE_READ((" sizing for %zu elems ", len)); break; case CFG_GROUP: block = calloc(1, desc->size); *(void**)(((char*)target) + desc->offset) = block; TRACE_READ((" sizing for %zu elems ", len)); break; default: /* scalar types: copy default */ memcpy(((char*)target) + desc->offset, &desc->default_val, desc->size); TRACE_READ(("setting %s to default ", desc->name)); print_setting(desc->type,(char*)target + desc->offset); break; } } static int read_block(config_setting_t* cfg, void* target, struct config_desc* desc, char** errmsg); /* When traversing configuration, set value from config * file, or command line * return: 0 if not set, 1 if set somehow */ static int read_block_setval(void* target, config_setting_t* cfg, struct config_desc* desc, char** errmsg) { int i; size_t len = 0; void* block; int in_cfg = 0, in_cl = 0; /* Present in config file? present on command line? */ config_setting_t* setting = NULL; switch (desc->type) { case CFG_LIST: if (cfg) { setting = config_setting_lookup_ud(cfg, desc); if (setting) len = config_setting_length(setting); block = *(void**)(((char*)target) + desc->offset); for (i = 0; i < len; i++) { config_setting_t* elem = config_setting_get_elem(setting, i); if (!read_block(elem, (char*)block + desc->size * i, desc->sub_group, errmsg)) return 0; } } break; case CFG_ARRAY: if (cfg) { setting = config_setting_lookup_ud(cfg, desc); if (setting) len = config_setting_length(setting); block = *(void**)(((char*)target) + desc->offset); for (i = 0; i < len; i++) { config_setting_t* elem = config_setting_get_elem(setting, i); settingcpy(desc->array_type, (char*)block + desc->size * i, elem); TRACE_READ(("[%d] = ", i)); print_setting(desc->array_type, (char*)block + desc->size *i); TRACE_READ(("\n")); } setting_delete_ud(cfg, desc); } break; case CFG_GROUP: if (cfg) setting = config_setting_lookup_ud(cfg, desc); block = *(void**)(((char*)target) + desc->offset); if (!read_block(setting, block, desc->sub_group, errmsg)) return 0; break; default: /* scalar types */ TRACE_READ((" `%s'", desc->name)); if (cfg && config_setting_lookup_ud(cfg, desc)) { TRACE_READ((" in config file: ")); /* setting is present in cfg, look it up */ if (lookup_typed_ud(cfg, target, desc) != CONFIG_TRUE) { TRACE_READ((" but wrong type (expected %s) ", type2str[desc->type])); asprintf(errmsg, "Option \"%s\" wrong type, expected %s\n", desc->name, type2str[desc->type]); return 0; } print_setting(desc->type, (((char*)target) + desc->offset)); setting_delete_ud(cfg, desc); in_cfg = 1; } else { TRACE_READ((" not in config file")); } if (desc->arg_cl && (*(struct arg_int**)desc->arg_cl)->count) { clcpy(desc->type, ((char*)target) + desc->offset, desc->arg_cl); TRACE_READ((", command line sets to ")); print_setting(desc->type, (((char*)target) + desc->offset)); in_cl = 1; } else { TRACE_READ((", not in command line")); } if (!(in_cfg || in_cl)) { TRACE_READ(("\n")); return 0; } TRACE_READ(("\n")); break; } return 1; } /* Set *_is_present flag for target */ static void target_is_present(void* target, struct config_desc* desc, int val) { if (desc->optional) { /* _is_present only exists in target for optional settings */ TRACE_READ((" mark as set")); *(int*)((char*)target + desc->offset_present) = val; } } /* traverses the configuration; allocates memory if needed, * set to default if exists, * fill from configuration file if present, overrides or set from * command line if present, verifies mandatory options have * been set * target: base address of the group being processed */ static int read_block(config_setting_t* cfg, void* target, struct config_desc* desc, char** errmsg) { int set; for (; desc->name; desc++) { TRACE_READ(("reading %s%s%s: ", desc->optional ? "optional " : "", desc->mandatory ? "mandatory " : "", desc->name)); desc->base_addr = target; read_block_init(target, cfg, desc); set = read_block_setval(target, cfg, desc, errmsg); if (!set && desc->mandatory) { asprintf(errmsg, "Mandatory option \"%s\" not found", desc->name); return 0; } if (desc->optional) target_is_present(target, desc, set && desc->optional); } return 1; } /* Copy regex match into newly allocated string * out: newly allocated string (caller has to free it) * in: string into which the match was made * pmatch: the match to extract */ static void pmatchcpy(char** out, const char* in, regmatch_t* pmatch) { int len = pmatch->rm_eo - pmatch->rm_so; *out = calloc(len+1, 1); memcpy(*out, in + pmatch->rm_so, len); } /* Processes a list of targets within one element, setting * the values in the target setting * target: where to put the data * arg: CL arg containing the target fields * clval: command line parameter * pmatch: regex match array into clval */ static int set_target_fields(void* target_addr, struct compound_cl_arg* arg, const char* clval, regmatch_t* pmatch) { int pmatch_cnt = 1; struct compound_cl_target* target; for (target = arg->targets; target->element; target++) { struct config_desc * element = target->element; if (target->match) { TRACE_CMPD((" match %d rm_so %d rm_eo %d type %d\n", pmatch_cnt, pmatch[pmatch_cnt].rm_so, pmatch[pmatch_cnt].rm_eo, element->type )); if (pmatch[pmatch_cnt].rm_so == -1) { /* This should not happen as regexec() did * match before, unless there is a * discrepancy between the regex and the * number of backreferences */ return 0; } char* str; pmatchcpy(&str, clval, &pmatch[pmatch_cnt]); stringcpy(element->type, (char*)target_addr + element->offset, str); TRACE_CMPD(("setting %p+%zu to : ", target_addr , element->offset)); print_setting(element->type , (char*)target_addr + element->offset); TRACE_CMPD(("\n")); /* str is temporary buffer for type conversion, except for strings which we * need to keep around so don't free them */ if (element->type != CFG_STRING) free(str); pmatch_cnt++; } else { /* don't use matching, set constant */ any_valcpy(element->type, (char*)target_addr + element->offset, target->value); } } return 1; } /* Goes over a list, finds if a group matches the specified string and overwrite * it if it does. */ static int override_on_str(struct compound_cl_arg* arg, const char* str, regmatch_t* pmatch) { struct config_desc * desc = arg->base_entry; void* list_base = *(void**)(desc->base_addr + desc->offset); size_t list_len = *(size_t*)(desc->base_addr + desc->offset_len); size_t elem_size = desc->size; int i; for (i = 0; i < list_len; i++) { char* group_base = ((char*)list_base + i * elem_size); char* cfg_member = *(char**)(group_base + arg->override_desc->offset); if (!strcmp(str, cfg_member)) { memset(group_base, 0, elem_size); struct arg_str* arg_cl = *arg->arg_cl; if (!set_target_fields(group_base, arg, arg_cl->sval[0], pmatch)) return 0; return 1; } } return 0; } /* Goes over a list and override group if needed */ static int override_elem(struct compound_cl_arg* arg, int arg_index, regmatch_t* pmatch) { char* str; int allocated = 0; int res; if (arg->override_matchindex) { struct arg_str* arg_cl = *arg->arg_cl; pmatchcpy(&str, arg_cl->sval[arg_index], &pmatch[arg->override_matchindex]); allocated = 1; } else { str = arg->override_const; } res = override_on_str(arg, str, pmatch); if (allocated) free(str); return res; } /* Add an argument to a list, overriding if required or * appending otherwise */ static int add_arg_to_list(struct compound_cl_arg* arg, int arg_index, regmatch_t* pmatch) { struct config_desc * desc = arg->base_entry; void* list_base = *(void**)(desc->base_addr + desc->offset); size_t list_len = *(size_t*)(desc->base_addr + desc->offset_len); size_t elem_size = desc->size; /* are we overriding an existing group? */ if (arg->override_desc) if (override_elem(arg, arg_index, pmatch)) return 1; /* override not found or no override, append element and * zero it out */ list_len++; list_base = realloc(list_base, list_len * elem_size); *(size_t*)(desc->base_addr + desc->offset_len) = list_len; *(void**)(desc->base_addr + desc->offset) = list_base; memset(list_base + (list_len - 1) * elem_size, 0, elem_size); struct arg_str* arg_cl = *arg->arg_cl; if (!set_target_fields((char*)list_base + (list_len - 1) * elem_size, arg, arg_cl->sval[arg_index], pmatch)) { return 0; } return 1; } /* TODO: pass pmatch size as parameter or something */ #define MAX_MATCH 10 #ifndef LIBPCRE static int regcompmatch_posix( regmatch_t* pmatch, struct compound_cl_arg* arg, int arg_index, char** errmsg) { char* regerr; struct arg_str* arg_cl = *arg->arg_cl; regex_t preg; int res = regcomp(&preg, arg->regex, REG_EXTENDED); if (res) { int errlen = regerror(res, &preg, NULL, 0); regerr = malloc(errlen); regerror(res, &preg, regerr, errlen); asprintf(errmsg, "compiling pattern /%s/:%s", arg->regex, regerr); free(regerr); return 0; } res = regexec(&preg, arg_cl->sval[arg_index], MAX_MATCH, &pmatch[0], 0); if (res) { asprintf(errmsg, "--%s %s: Illegal argument", arg_cl->hdr.longopts, arg->regex); return 0; } return 1; } #endif #ifdef LIBPCRE static int regcompmatch_pcre2( regmatch_t* pmatch, struct compound_cl_arg* arg, int arg_index, char** errmsg) { int i, error; pcre2_code* pcre; PCRE2_UCHAR8 err_str[120]; /* ample, according to pcre2api(3) */ pcre2_match_data* matches; PCRE2_SIZE error_offset; struct arg_str* arg_cl = *arg->arg_cl; pcre = pcre2_compile((PCRE2_SPTR8)arg->regex, PCRE2_ZERO_TERMINATED, 0, &error, &error_offset, NULL); if (!pcre) { pcre2_get_error_message(error, err_str, sizeof(err_str)); asprintf(errmsg, "compiling pattern /%s/:%d: %s at offset %ld\n", arg->regex, error, err_str, error_offset); return 0; } matches = pcre2_match_data_create(MAX_MATCH, NULL); int res = pcre2_match(pcre, (PCRE2_SPTR8)arg_cl->sval[arg_index], PCRE2_ZERO_TERMINATED, 0, 0, matches, NULL); if (res < 0) { pcre2_get_error_message(res, err_str, sizeof(err_str)); asprintf(errmsg, "matching %s =~ /%s/:%d: %s\n", arg_cl->sval[arg_index], arg->regex, res, err_str); return 0; } PCRE2_SIZE *ovec = pcre2_get_ovector_pointer(matches); if (res > MAX_MATCH) res = MAX_MATCH; /* From pcre2posix.c */ for (i = 0; i < res; i++) { pmatch[i].rm_so = (ovec[i*2] == PCRE2_UNSET) ? -1 : ovec[i*2]; pmatch[i].rm_eo = (ovec[i*2+1] == PCRE2_UNSET) ? -1 : ovec[i*2+1]; } for (; i < MAX_MATCH; i++) pmatch[i].rm_so = pmatch[i].rm_eo = -1; pcre2_match_data_free(matches); return 1; } #endif /* Regex fiddling: uses info in arg to fill pmatch * arg: description of the command line argument * arg_index: occurence of this argument on the command line */ static int regcompmatch(regmatch_t* pmatch, struct compound_cl_arg* arg, int arg_index, char** errmsg) { #if LIBPCRE return regcompmatch_pcre2(pmatch, arg, arg_index, errmsg); #else return regcompmatch_posix(pmatch, arg, arg_index, errmsg); #endif } /* Read compound options described in `arg`, from `cfg`, to `setting` */ static int read_compounds(config_setting_t* cfg, void* setting, struct compound_cl_arg* arg, char** errmsg) { int arg_i; struct arg_str* arg_cl; regmatch_t pmatch[MAX_MATCH]; for (; arg->regex; arg++) { arg_cl = *arg->arg_cl; TRACE_CMPD(("Compound %s occurs %d : ", arg_cl->hdr.longopts, arg_cl->count)); for (arg_i = 0; arg_i < arg_cl->count; arg_i++) { if (!regcompmatch(&pmatch[0], arg, arg_i, errmsg)) return 0; TRACE_CMPD(("`%s' matched\n", arg_cl->sval[arg_i])); switch (arg->base_entry->type) { case CFG_LIST: /* In a list, find the end or the element to override */ if (!add_arg_to_list(arg, arg_i, pmatch)) { return 0; } break; /* Semantics for CFG_ARRAY TBD */ case CFG_GROUP: if (!set_target_fields( /* base_addr is the same for all elements in the group */ arg->targets[0].element->base_addr, arg, arg_cl->sval[arg_i], pmatch)) return 0; default: TRACE_CMPD(("error, compound on type %d\n", arg->base_entry->type)); break; } } TRACE_CMPD(("done %s\n", arg_cl->hdr.longopts)); } return 1; } /* read config file `filename` into `c` */ static int c2s_parse_file(const char* filename, config_t* c, char**errmsg) { /* Read config file */ if (config_read_file(c, filename) == CONFIG_FALSE) { if (config_error_line(c) != 0) { asprintf(errmsg, "%s:%d:%s", filename, config_error_line(c), config_error_text(c)); return 0; } asprintf(errmsg, "%s:%s", filename, config_error_text(c)); return 0; } return 1; } /* Allocates a new string that represents the setting value, which must be a scalar */ static void scalar_to_string(char** strp, config_setting_t* s) { switch(config_setting_type(s)) { case CONFIG_TYPE_INT: asprintf(strp, "%d\n", config_setting_get_int(s)); break; case CONFIG_TYPE_BOOL: asprintf(strp, "%s\n", config_setting_get_bool(s) ? "[true]" : "[false]" ); break; case CONFIG_TYPE_INT64: asprintf(strp, "%lld\n", config_setting_get_int64(s)); break; case CONFIG_TYPE_FLOAT: asprintf(strp, "%lf\n", config_setting_get_float(s)); break; case CONFIG_TYPE_STRING: asprintf(strp, "%s\n", config_setting_get_string(s)); break; default: /* This means a bug */ fprintf(stderr, "Unexpected type %d\n", config_setting_type(s)); exit(1); } } /* Typesets all the settings in a configuration as a * newly-allocated string. The string management is caller's * responsibility. * Returns the number of scalars in the configuration */ static int cfg_as_string(config_setting_t* parent, const char* path, char** strp) { int i, len, res = 0; config_setting_t* child; char* subpath, *value, *old; const char* name; len = config_setting_length(parent); for (i = 0; i < len; i++) { child = config_setting_get_elem(parent, i); name = config_setting_name(child); if (!name) name = ""; if(config_setting_is_list(parent) || config_setting_is_array(parent)) { asprintf(&subpath, "%s[%d]%s", path, config_setting_index(child), name); } else { asprintf(&subpath, "%s/%s", path, name); } if (config_setting_is_scalar(child)) { scalar_to_string(&value, child); /* Add value to the output string */ if (*strp) { asprintf(&old, "%s", *strp); free(*strp); } else { asprintf(&old, "%s", ""); } asprintf(strp, "%s%s:%s", old, subpath, value); free(value); free(old); res++; /* At least one scalar was found */ } else { /* It's an aggregate -- descend into it */ res += cfg_as_string(child, subpath, strp); } free(subpath); } return res; } /* 0: success <0: error */ int echocfg_cl_parse(int argc, char* argv[], struct echocfg_item* cfg) { int nerrors, res; config_t c; char* errmsg; config_setting_t* s; void* argtable[] = { #ifdef LIBCONFIG echocfg_conffile = arg_filen("F", "config", "", 0, 1, "Specify configuration file"), #endif echocfg_udp = arg_litn(NULL, "udp", 0, 1, ""), echocfg_prefix = arg_strn(NULL, "prefix", "", 0, 1, ""), echocfg_listen_host = arg_strn(NULL, "listen-host", "", 0, 10, ""), echocfg_listen_port = arg_strn(NULL, "listen-port", "", 0, 10, ""), echocfg_listen = arg_strn("p", "listen", "", 0, 10, "Listen on host:port"), echocfg_end = arg_end(10) }; /* Parse command line */ nerrors = arg_parse(argc, argv, argtable); if (nerrors) { arg_print_errors(stdout, echocfg_end, "echocfg"); arg_print_syntax(stdout, argtable, "\n"); arg_print_glossary(stdout, argtable, " %-25s\t%s\n"); return -1; } config_init(&c); if (echocfg_conffile && echocfg_conffile->count) { if (!c2s_parse_file(echocfg_conffile->filename[0], &c, &errmsg)) { fprintf(stderr, "%s\n", errmsg); return -1; } } s = config_root_setting(&c); res = read_block(s, cfg, table_echocfg, &errmsg); if (!res) { fprintf(stderr, "%s\n", errmsg); return -1; } res = read_compounds(s, cfg, compound_cl_args, &errmsg); if (!res) { fprintf(stderr, "%s\n", errmsg); return -1; } errmsg = NULL; res = cfg_as_string(s, "", &errmsg); if (res) fprintf(stderr, "Unknown settings:\n%s\n", errmsg); return 0; } static void indent(FILE* out, int depth) { int i; for (i = 0; i < depth; i++) fprintf(out, " "); } static void echocfg_listen_fprint( FILE* out, struct echocfg_listen_item* echocfg_listen, int depth) { indent(out, depth); fprintf(out, "host: %s", echocfg_listen->host); fprintf(out, "\n"); indent(out, depth); fprintf(out, "port: %s", echocfg_listen->port); fprintf(out, "\n"); } void echocfg_fprint( FILE* out, struct echocfg_item* echocfg, int depth) { int i; indent(out, depth); fprintf(out, "udp: %d", echocfg->udp); fprintf(out, "\n"); indent(out, depth); fprintf(out, "prefix: %s", echocfg->prefix); fprintf(out, "\n"); indent(out, depth); fprintf(out, "listen [%zu]:\n", echocfg->listen_len); for (i = 0; i < echocfg->listen_len; i++) { echocfg_listen_fprint(out, &echocfg->listen[i], depth+1); } } sslh-2.1.4/echosrv-conf.h000066400000000000000000000040651463704647400152600ustar00rootroot00000000000000/* Generated by conf2struct (https://www.rutschle.net/tech/conf2struct/README) * on Sat Apr 30 09:55:03 2022. # conf2struct: generate libconf parsers that read to structs # Copyright (C) 2018-2021 Yves Rutschle # All rights reserved. # # Redistribution and use in source and binary forms, with or without # modification, are permitted provided that the following conditions are met: # # 1. Redistributions of source code must retain the above copyright notice, # this list of conditions and the following disclaimer. # 2. Redistributions in binary form must reproduce the above copyright # notice, this list of conditions and the following disclaimer in the # documentation and/or other materials provided with the distribution. # # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" # AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE # IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE # ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE # LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR # CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF # SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS # INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN # CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) # ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE # POSSIBILITY OF SUCH DAMAGE. */ #ifndef C2S_ECHOCFG_H #define C2S_ECHOCFG_H #ifdef LIBCONFIG # include #endif struct echocfg_listen_item { char* host; char* port; }; struct echocfg_item { int udp; char* prefix; size_t listen_len; struct echocfg_listen_item* listen; }; int echocfg_parse_file( const char* filename, struct echocfg_item* echocfg, const char** errmsg); void echocfg_fprint( FILE* out, struct echocfg_item *echocfg, int depth); int echocfg_cl_parse( int argc, char* argv[], struct echocfg_item *echocfg); #endif sslh-2.1.4/echosrv.c000066400000000000000000000217661463704647400143370ustar00rootroot00000000000000/* echosrv: a simple line echo server with optional prefix adding. * * echosrv --listen localhost6:1234 --prefix "ssl: " * * This will bind to 1234, and echo every line pre-pending "ssl: ". This is * used for testing: we create several such servers with different prefixes, * then we connect test clients that can then check they get the proper data * back (thus testing that shoveling works both ways) with the correct prefix * (thus testing it connected to the expected service). * **/ #define _GNU_SOURCE #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #define cfg sslhcfg #include "common.h" #undef cfg #include "echosrv-conf.h" /* Added to make the code compilable under CYGWIN * */ #ifndef SA_NOCLDWAIT #define SA_NOCLDWAIT 0 #endif struct echocfg_item cfg; void check_res_dump(int res, struct addrinfo *addr, char* syscall) { char buf[NI_MAXHOST]; if (res == -1) { if (addr) fprintf(stderr, "error %s:%s: %s\n", sprintaddr(buf, sizeof(buf), addr), syscall, strerror(errno)); else fprintf(stderr, "Dying just because\n"); exit(1); } } void start_echo(int fd) { ssize_t res; char buffer[1 << 20]; ssize_t ret; size_t prefix_len; int first = 1; prefix_len = strlen(cfg.prefix); memset(buffer, 0, sizeof(buffer)); strcpy(buffer, cfg.prefix); while (1) { ret = read(fd, buffer + prefix_len, sizeof(buffer) - prefix_len); if (ret <= 0) { fprintf(stderr, "%s", strerror(errno)); return; } if (first) { res = write(fd, buffer, ret + prefix_len); first = 0; if (write(1, buffer, ret + prefix_len) < 0) { fprintf(stderr, "%s", strerror(errno)); } } else { res = write(fd, buffer + prefix_len, ret); } if (res < 0) { fprintf(stderr, "%s", strerror(errno)); return; } } } /* TCP echo server: accepts connections to an endpoint, forks an echo for each * connection, forever. Prefix is added at start of response stream */ void tcp_echo(struct listen_endpoint* listen_socket) { while (1) { int in_socket = accept(listen_socket->socketfd, 0, 0); if (in_socket == -1) { perror("tcp_echo:accept"); exit(1); } if (!fork()) { close(listen_socket->socketfd); start_echo(in_socket); exit(0); } close(in_socket); waitpid(-1, NULL, WNOHANG); } } void print_udp_xchange(int sockfd, struct sockaddr* addr, socklen_t addrlen) { struct addrinfo src_addrinfo, to_addrinfo; char str_addr[NI_MAXHOST+1+NI_MAXSERV+1]; char str_addr2[NI_MAXHOST+1+NI_MAXSERV+1]; struct sockaddr_storage ss; src_addrinfo.ai_addr = (struct sockaddr*)&ss; src_addrinfo.ai_addrlen = sizeof(ss); getsockname(sockfd, src_addrinfo.ai_addr, &src_addrinfo.ai_addrlen); to_addrinfo.ai_addr = addr; to_addrinfo.ai_addrlen = sizeof(*addr); fprintf(stderr, "UDP local %s remote %s\n", sprintaddr(str_addr, sizeof(str_addr), &src_addrinfo), sprintaddr(str_addr2, sizeof(str_addr2), &to_addrinfo) ); } /* UDP echo server: receive packets, return them, forever. * Prefix is added at each packet */ void udp_echo(struct listen_endpoint* listen_socket) { char data[65536]; struct sockaddr src_addr; socklen_t addrlen; memset(data, 0, sizeof(data)); size_t prefix_len = strlen(cfg.prefix); memcpy(data, cfg.prefix, prefix_len); while (1) { addrlen = sizeof(src_addr); ssize_t len = recvfrom(listen_socket->socketfd, data + prefix_len, sizeof(data) - prefix_len, 0, &src_addr, &addrlen); if (len < 0) { perror("recvfrom"); } *(data + prefix_len + len) = 0; fprintf(stderr, "%zd %s\n", len, data + prefix_len); print_udp_xchange(listen_socket->socketfd, &src_addr, addrlen); ssize_t res = sendto(listen_socket->socketfd, data, len + prefix_len, 0, &src_addr, addrlen); if (res < 0) { perror("sendto"); } } } void main_loop(struct listen_endpoint listen_sockets[], int num_addr_listen) { int i; for (i = 0; i < num_addr_listen; i++) { if (!fork()) { if (cfg.udp) { udp_echo(&listen_sockets[i]); } else { tcp_echo(&listen_sockets[i]); } } } wait(NULL); } /* Following is a number of utility functions copied from common.c: linking * against common.o directly means echosrv has to work with sslh config struct, * which makes it all too awkward */ /* simplified from common.c */ char* sprintaddr(char* buf, size_t size, struct addrinfo *a) { char host[NI_MAXHOST], serv[NI_MAXSERV]; int res; res = getnameinfo(a->ai_addr, a->ai_addrlen, host, sizeof(host), serv, sizeof(serv), 0 ); if (res) { /* Name resolution failed: do it numerically instead */ res = getnameinfo(a->ai_addr, a->ai_addrlen, host, sizeof(host), serv, sizeof(serv), NI_NUMERICHOST | NI_NUMERICSERV); /* should not fail but... */ if (res) { strcpy(host, "?"); strcpy(serv, "?"); } } snprintf(buf, size, "%s:%s", host, serv); return buf; } /* simplified from common.c */ int listen_single_addr(struct addrinfo* addr, int keepalive, int udp) { struct sockaddr_storage *saddr; int sockfd, one, res; saddr = (struct sockaddr_storage*)addr->ai_addr; sockfd = socket(saddr->ss_family, udp ? SOCK_DGRAM : SOCK_STREAM, 0); check_res_dump(sockfd, addr, "socket"); one = 1; res = setsockopt(sockfd, SOL_SOCKET, SO_REUSEADDR, (char*)&one, sizeof(one)); check_res_dump(res, addr, "setsockopt(SO_REUSEADDR)"); if (addr->ai_addr->sa_family == AF_INET6) { res = setsockopt(sockfd, IPPROTO_IPV6, IPV6_V6ONLY, (char*)&one, sizeof(one)); check_res_dump(res, addr, "setsockopt(IPV6_V6ONLY)"); } res = bind(sockfd, addr->ai_addr, addr->ai_addrlen); check_res_dump(res, addr, "bind"); if (!udp) { res = listen (sockfd, 50); check_res_dump(res, addr, "listen"); } return sockfd; } /* simplified from common.c */ int resolve_split_name(struct addrinfo **out, char* host, char* serv) { struct addrinfo hint; char *end; int res; memset(&hint, 0, sizeof(hint)); hint.ai_family = PF_UNSPEC; hint.ai_socktype = SOCK_STREAM; /* If it is a RFC-Compliant IPv6 address ("[1234::12]:443"), remove brackets * around IP address */ if (host[0] == '[') { end = strrchr(host, ']'); if (!end) { fprintf(stderr, "%s: no closing bracket in IPv6 address?\n", host); return -1; } host++; /* skip first bracket */ *end = 0; /* remove last bracket */ } res = getaddrinfo(host, serv, &hint, out); if (res) fprintf(stderr, "%s `%s:%s'\n", gai_strerror(res), host, serv); return res; } int start_listen_sockets(struct listen_endpoint *sockfd[]) { struct addrinfo *addr, *start_addr; char buf[NI_MAXHOST]; int i, res; int num_addr = 0, keepalive = 0, udp = 0; *sockfd = NULL; fprintf(stderr, "Listening to:\n"); for (i = 0; i < cfg.listen_len; i++) { udp = cfg.udp; res = resolve_split_name(&start_addr, cfg.listen[i].host, cfg.listen[i].port); if (res) exit(4); for (addr = start_addr; addr; addr = addr->ai_next) { num_addr++; *sockfd = realloc(*sockfd, num_addr * sizeof(*sockfd)); (*sockfd)[num_addr-1].socketfd = listen_single_addr(addr, keepalive, udp); (*sockfd)[num_addr-1].type = udp ? SOCK_DGRAM : SOCK_STREAM; fprintf(stderr, "%d:\t%s\n", (*sockfd)[num_addr-1].socketfd, sprintaddr(buf, sizeof(buf), addr)); } freeaddrinfo(start_addr); } return num_addr; } int main(int argc, char *argv[]) { extern char *optarg; extern int optind; int num_addr_listen; struct listen_endpoint *listen_sockets; memset(&cfg, 0, sizeof(cfg)); if (echocfg_cl_parse(argc, argv, &cfg)) exit(1); echocfg_fprint(stdout, &cfg, 0); num_addr_listen = start_listen_sockets(&listen_sockets); main_loop(listen_sockets, num_addr_listen); return 0; } sslh-2.1.4/echosrv.cfg000066400000000000000000000017271463704647400146470ustar00rootroot00000000000000# conf2struct for echosrv header: "echosrv-conf.h"; parser: "echosrv-conf.c"; printer: true; conffile_option: ("F", "config"); config: { name: "echocfg", type: "list", items: ( {name: "udp", type: "bool"; default: false; }, {name: "prefix", type: "string"; }, { name: "listen", type: "list", items: ( { name: "host"; type: "string"; var: true; }, { name: "port"; type: "string"; var: true; } ) } ) } cl_groups: ( { name: "listen"; pattern: "(.+):(\w+)"; description: "Listen on host:port"; short: "p"; argdesc: ""; list: "listen"; # no override, this just adds to the list (and thus can be specified several times) targets: ( { path: "host"; value: "$1" }, { path: "port"; value: "$2" } ); } ) sslh-2.1.4/echoѕrv-conf.h000066400000000000000000000055641463704647400156500ustar00rootroot00000000000000/* Generated by conf2struct (https://www.rutschle.net/tech/conf2struct/README) * on Sat Nov 7 09:19:26 2020. # conf2struct: generate libconf parsers that read to structs # Copyright (C) 2018-2019 Yves Rutschle # All rights reserved. # # Redistribution and use in source and binary forms, with or without # modification, are permitted provided that the following conditions are met: # # 1. Redistributions of source code must retain the above copyright notice, # this list of conditions and the following disclaimer. # 2. Redistributions in binary form must reproduce the above copyright # notice, this list of conditions and the following disclaimer in the # documentation and/or other materials provided with the distribution. # # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" # AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE # IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE # ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE # LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR # CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF # SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS # INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN # CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) # ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE # POSSIBILITY OF SUCH DAMAGE. */ #ifndef C2S_SSLHCFG_H #define C2S_SSLHCFG_H #ifdef LIBCONFIG # include #endif #include "probe.h" #include #include #include struct sslhcfg_listen_item { char* host; char* port; int keepalive; }; struct sslhcfg_protocols_item { char* name; char* host; char* port; int service_is_present; char* service; int fork; int tfo_ok; int log_level; int keepalive; size_t sni_hostnames_len; char** sni_hostnames; size_t alpn_protocols_len; char** alpn_protocols; size_t regex_patterns_len; char** regex_patterns; int minlength_is_present; int minlength; T_PROBE* probe; struct addrinfo* saddr; void* data; }; struct sslhcfg_item { char* prefix; int verbose; int foreground; int inetd; int numeric; int transparent; int timeout; int user_is_present; char* user; int pidfile_is_present; char* pidfile; int chroot_is_present; char* chroot; char* syslog_facility; char* on_timeout; size_t listen_len; struct sslhcfg_listen_item* listen; size_t protocols_len; struct sslhcfg_protocols_item* protocols; }; int sslhcfg_parse_file( const char* filename, struct sslhcfg_item* sslhcfg, const char** errmsg); void sslhcfg_fprint( FILE* out, struct sslhcfg_item *sslhcfg, int depth); int sslhcfg_cl_parse( int argc, char* argv[], struct sslhcfg_item *sslhcfg); #endif sslh-2.1.4/example.cfg000066400000000000000000000147621463704647400146340ustar00rootroot00000000000000# This file is provided as documentation to show what is # possible. It should not be used as-is, and probably should # not be used as a starting point for a working # configuration. Instead use basic.cfg. foreground: true; inetd: false; numeric: false; transparent: false; timeout: 2; user: "nobody"; pidfile: "/var/run/sslh.pid"; chroot: "/var/empty"; # Logging configuration # Value: 1: stdout; 2: syslog; 3: stdout+syslog; 4: logfile; ...; 7: all # Defaults are indicated here, and should be sensible. Generally, you want *-error # to be always enabled, to know if something is going wrong. verbose-config: 0; # print configuration at startup verbose-config-error: 3; # print configuration errors verbose-connections: 3; # trace established incoming address to forward address verbose-connections-error: 3; # connection errors verbose-connections-try: 0; # connection attempts towards targets verbose-fd: 0; # file descriptor activity, open/close/whatnot verbose-packets: 0; # hexdump packets on which probing is done verbose-probe-info: 0; # what's happening during the probe process verbose-probe-error: 3; # failures and problems during probing verbose-system-error: 3; # system call problem, i.e. malloc, fork, failing verbose-int-error: 3; # internal errors, the kind that should never happen # Specify a path to the logfile. #logfile: "/var/log/sslh.log" # Specify the number of concurrent UDP connection that can # be managed (default 1024) udp_max_connections: 16; # Specify which syslog facility to use (names for your # system are usually defined in /usr/include/*/sys/syslog.h # or equivalent) # Default is "auth" # "none" disables use of syslog syslog_facility: "auth"; # List of interfaces on which we should listen # Options: listen: ( { host: "thelonious"; port: "443"; }, { host: "thelonious"; port: "8080"; keepalive: true; }, { host: "thelonious"; is_udp: true; port: "443" } ); # List of protocols # # Each protocol entry consists of: # name: name of the probe. These are listed on the command # line (ssh -?), plus 'regex' and 'timeout'. # service: (optional) libwrap service name (see hosts_access(5)) # host, port: where to connect when this probe succeeds # log_level: 0 to turn off logging # 1 to log each incoming connection # keepalive: Should TCP keepalive be on or off for that # connection (default is off) # fork: Should a new process be forked for this protocol? # (only useful for sslh-select) # tfo_ok: Set to true if the server supports TCP FAST OPEN # resolve_on_forward: Set to true if server address should be resolved on # (every) newly incoming connection (again) # transparent: Set to true to proxy this protocol # transparently (server sees the remote client IP # address). Same as the global option, but per-protocol # # Probe-specific options: # (sslh will try each probe in order they are declared, and # connect to the first that matches.) # # tls: # sni_hostnames: list of FQDN for that target. Each name can # include wildcard following glob(7) rules. # alpn_protocols: list of ALPN protocols for that target, see: # https://www.iana.org/assignments/tls-extensiontype-values/tls-extensiontype-values.xhtml#alpn-protocol-ids # # if both sni_hostnames AND alpn_protocols are specified, both must match # # if neither are set, it is just checked whether this is the TLS protocol or not # # Obviously set the most specific probes # first, and if you use TLS with no ALPN/SNI # set it as the last TLS probe # regex: # regex_patterns: list of patterns to match for # that target. # # You can specify several of 'regex' and 'tls'. protocols: ( { name: "ssh"; service: "ssh"; host: "localhost"; port: "22"; keepalive: true; fork: true; tfo_ok: true }, { name: "http"; host: "localhost"; port: "80"; }, # match BOTH ALPN/SNI { name: "tls"; host: "localhost"; port: "5223"; alpn_protocols: [ "xmpp-client" ]; sni_hostnames: [ "im.somethingelse.net" ]; log_level: 0; tfo_ok: true }, # just match ALPN { name: "tls"; host: "localhost"; port: "443"; alpn_protocols: [ "h2", "http/1.1", "spdy/1", "spdy/2", "spdy/3" ]; log_level: 0; tfo_ok: true }, { name: "tls"; host: "localhost"; port: "xmpp-client"; alpn_protocols: [ "xmpp-client" ]; log_level: 0; tfo_ok: true }, # just match SNI { name: "tls"; host: "localhost"; port: "993"; sni_hostnames: [ "mail.rutschle.net", "mail.englishintoulouse.com" ]; log_level: 0; tfo_ok: true }, { name: "tls"; host: "localhost"; port: "xmpp-client"; sni_hostnames: [ "im.rutschle.net", "im.englishintoulouse.com" ]; log_level: 0; tfo_ok: true }, # Let's Encrypt (tls-alpn-* challenges) { name: "tls"; host: "localhost"; port: "letsencrypt-client"; alpn_protocols: [ "acme-tls/1" ]; log_level: 0;}, # catch anything else TLS { name: "tls"; host: "localhost"; port: "443"; tfo_ok: true }, # Forward UDP { name: "regex"; host: "localhost"; is_udp: true; port: "123"; udp_timeout: 20; # Time after which the "connection" is forgotten regex_patterns: [ "hello" ]; }, # Forward Teamspeak3 (Voice only) { name: "teamspeak"; host: "localhost"; is_udp: true; port: "9987"; }, # Forward IETF QUIC-50 ("Q050" -> "\x51\x30\x35\x30") # Remember that the regex needs to be adjusted for every supported QUIC version. { name: "regex"; host: "localhost"; is_udp: true; port: "4433"; regex_patterns: [ "\x51\x30\x35\x30" ]; }, # Regex examples -- better use the built-in probes for real-world use! # OpenVPN { name: "regex"; host: "localhost"; port: "1194"; regex_patterns: [ "^\x00[\x0D-\xFF]$", "^\x00[\x0D-\xFF]\x38" ]; }, # Jabber { name: "regex"; host: "localhost"; port: "5222"; regex_patterns: [ "jabber" ]; minlength: 60; # Won't even try to match the regex if we don't have that many bytes }, # Catch-all (but better use 'anyprot') { name: "regex"; host: "localhost"; port: "443"; regex_patterns: [ "" ]; }, # Where to connect in case of timeout (defaults to ssh) { name: "timeout"; service: "daytime"; host: "localhost"; port: "daytime"; } ); # Optionally, specify to which protocol to connect in case # of timeout (defaults to "ssh"). # You can timeout to any arbitrary address by setting an # entry in 'protocols' named "timeout". # This enables you to set a tcpd service name for this # protocol too. on-timeout: "timeout"; sslh-2.1.4/gap.c000066400000000000000000000050371463704647400134260ustar00rootroot00000000000000/* gap.c: gap, a simple, dynamically-growing array of pointers that never shrinks # Copyright (C) 2021 Yves Rutschle # # 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. # # The full text for the General Public License is here: # http://www.gnu.org/licenses/gpl.html */ #include #include #include #include #include "sslh-conf.h" #include "gap.h" /* Allocate one page-worth of elements */ static int gap_len_alloc(int elem_size) { return getpagesize() / elem_size; } /* Creates a new gap at least `len` big, all pointers are initialised at NULL */ gap_array* gap_init(int len) { gap_array* gap = malloc(sizeof(*gap)); if (!gap) return NULL; memset(gap, 0, sizeof(*gap)); int elem_size = sizeof(gap->array[0]); gap->len = gap_len_alloc(elem_size); if (gap->len < len) gap->len = len; gap->array = malloc(gap->len * elem_size); if (!gap->array) { free(gap); return NULL; } for (int i = 0; i < gap->len; i++) gap->array[i] = NULL; return gap; } int gap_extend(gap_array* gap) { int elem_size = sizeof(gap->array[0]); int new_length = gap->len + gap_len_alloc(elem_size); void** new = realloc(gap->array, new_length * elem_size); if (!new) return -1; gap->array = new; for (int i = gap->len; i < new_length; i++) { gap->array[i] = NULL; } gap->len = new_length; return 0; } void gap_destroy(gap_array* gap) { free(gap->array); free(gap); } /* In gap, find element pointing to ptr, then shift the rest of the array that * is considered len elements long. * A poor man's list, if you will. Currently only used to remove probing * connections, so it only copies a few pointers at most. * Returns -1 if ptr was not found */ int gap_remove_ptr(gap_array* gap, void* ptr, int len) { int start, i; for (i = 0; i < len; i++) if (gap->array[i] == ptr) break; if (i < len) start = i; else return -1; for (i = start; i < len - 1; i++) { gap->array[i] = gap->array[i+1]; } return 0; } sslh-2.1.4/gap.h000066400000000000000000000021511463704647400134250ustar00rootroot00000000000000#ifndef GAP_H #define GAP_H typedef struct gap_array gap_array; gap_array* gap_init(int len); static void* gap_get(gap_array* gap, int index); static int gap_set(gap_array* gap, int index, void* ptr); void gap_destroy(gap_array* gap); int gap_remove_ptr(gap_array* gap, void* ptr, int len); /* Private declarations to allow inlining. * Don't assume my implementation. */ typedef struct gap_array { int len; /* Number of elements in array */ void** array; } gap_array; int gap_extend(gap_array* gap); static inline int __attribute__((unused)) gap_set(gap_array* gap, int index, void* ptr) { while (index >= gap->len) { int res = gap_extend(gap); if (res == -1) return -1; } gap->array[index] = ptr; return 0; } static inline void* __attribute__((unused)) gap_get(gap_array* gap, int index) { /* sslh-ev routinely reads before it writes. It's not clear if it should be * its job to check the length (and add a gap_getlen()), or if it should be * gap_get()'s job. This will do for now */ if (index >= gap->len) return NULL; return gap->array[index]; } #endif sslh-2.1.4/genver.sh000077500000000000000000000027111463704647400143340ustar00rootroot00000000000000#! /bin/sh if [ ${#} -eq 1 ] && [ "x$1" = "x-r" ]; then # release text only QUIET=1 else QUIET=0 fi if [ ! -d .git ] || ! `(git status | grep -q "On branch") 2> /dev/null`; then # If we don't have git, we can't work out what # version this is. It must have been downloaded as a # zip file. # If downloaded from the release page, the directory # has the version number. release=`pwd | sed s/.*sslh-// | grep "[[:digit:]]"` if [ "x$release" = "x" ]; then # If downloaded from the head, GitHub creates the # zip file with all files dated from the last # change: use the Makefile's modification time as a # release number release=head-`perl -MPOSIX -e 'print strftime "%Y-%m-%d",localtime((stat "Makefile")[9])'` fi fi if [ -d .git ] && head=`git rev-parse --verify HEAD 2>/dev/null`; then # generate the version info based on the tag release=`(git describe --tags || git --describe || git describe --all --long) \ 2>/dev/null | tr -s '/' '-' | tr -d '\n'` # Are there uncommitted changes? git update-index --refresh --unmerged > /dev/null if git diff-index --name-only HEAD | grep -v "^scripts/package" \ | read dummy; then release="$release-dirty" fi fi if [ $QUIET -ne 1 ]; then printf "#ifndef VERSION_H \n" printf "#define VERSION_H \n\n" printf "#define VERSION \"$release\"\n" printf "#endif\n" else printf "$release\n" fi sslh-2.1.4/hash.c000066400000000000000000000126741463704647400136070ustar00rootroot00000000000000/* * a fixed-sized hash * # Copyright (C) 2022 Yves Rutschle # # 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. # # The full text for the General Public License is here: # http://www.gnu.org/licenses/gpl.html # # */ /* * The hash is open-addressing, linear search, robin-hood insertion, with * backward shift deletion. References: * https://codecapsule.com/2013/11/11/robin-hood-hashing/ * https://codecapsule.com/2013/11/17/robin-hood-hashing-backward-shift-deletion/ * This means items are reordered upon insertion and deletion, and the hash * is well-ordered at all times with no tombstones. * * Each pointer is either: * - to a connection struct * - FREE (NULL) if not allocated * * */ #include #include #include "gap.h" typedef void* hash_item; #include "hash.h" static void* const FREE = NULL; struct hash { int hash_size; /* Max number of items in the hash */ int item_cnt; /* Number of items in the hash */ gap_array* data; hash_make_key_fn hash_make_key; hash_cmp_item_fn cmp_item; }; typedef struct hash hash; static int hash_make_key(hash* h, hash_item item) { return h->hash_make_key(item) % h->hash_size; } hash* hash_init(int hash_size, hash_make_key_fn make_key, hash_cmp_item_fn cmp_item) { hash* h = malloc(sizeof(*h)); if (!h) return NULL; h->hash_size = hash_size; h->item_cnt = 0; h->data = gap_init(hash_size); h->hash_make_key = make_key; h->cmp_item = cmp_item; return h; } /* Return the index following i in h */ static int hash_next_index(hash* h, int i) { return (i + 1) % h->hash_size; } /* Returns the index in h of specified address, -1 if not found * item is an item object that must return the target wanted index and for * which comparison with the searched object will succeed. * */ static int hash_find_index(hash* h, hash_item item) { hash_item cnx; int index = hash_make_key(h, item); int cnt = 0; cnx = gap_get(h->data, index); #ifdef DEBUG fprintf(stderr, "searching %d\n", index); #endif while (cnx != FREE) { if (cnt++ > h->hash_size) return -1; if (!h->cmp_item(cnx, item)) break; index = hash_next_index(h, index); cnx = gap_get(h->data, index); #ifdef DEBUG fprintf(stderr, "searching %d\n", index); #endif } if (cnx == FREE) return -1; return index; } hash_item hash_find(hash* h, hash_item item) { int index = hash_find_index(h, item); if (index == -1) return NULL; hash_item out = gap_get(h->data, index); return out; } /* Returns DIB: distance to initial bucket */ static int distance(int current_index, hash* h, hash_item item) { int wanted_index = hash_make_key(h, item); if (wanted_index <= current_index) return current_index - wanted_index; else return current_index - wanted_index + h->hash_size; } int hash_insert(hash* h, hash_item new) { int bubble_wanted_index = hash_make_key(h, new); int index = bubble_wanted_index; gap_array* hash = h->data; if (h->item_cnt == h->hash_size) return -1; hash_item curr_item = gap_get(hash, index); while (curr_item) { if (distance(index, h, curr_item) < distance(index, h, new)) { gap_set(h->data, index, new); #if DEBUG fprintf(stderr, "intermediate insert [%s] at %d\n", &new->client_addr, index); #endif new = curr_item; } index = hash_next_index(h, index); curr_item = gap_get(hash, index); } #if DEBUG fprintf(stderr, "final insert at %d\n", index); #endif gap_set(hash, index, new); h->item_cnt++; return 0; } /* Remove cnx from the hash */ int hash_remove(hash* h, hash_item item) { gap_array* hash = h->data; int index = hash_find_index(h, item); if (index == -1) return -1; /* Tried to remove something that isn't there */ while (1) { int next_index = hash_next_index(h, index); hash_item next = gap_get(h->data, next_index); if ((next == FREE) || (distance(next_index, h, next) == 0)) { h->item_cnt--; gap_set(hash, index, FREE); return 0; } gap_set(hash, index, next); index = hash_next_index(h, index);; } return 0; } #if HASH_TESTING #include #include #define STR_LENGTH 16 struct hash_item { int wanted_index; char str[STR_LENGTH]; }; void hash_dump(hash* h, char* filename) { char str[STR_LENGTH]; FILE* out = fopen(filename, "w"); if (!out) { perror(filename); exit(1); } fprintf(out, "\n", h->item_cnt); for (int i = 0; i < h->hash_size; i++) { hash_item item = gap_get(h->data, i); int idx = 0; memset(str, 0, STR_LENGTH); if (item) { idx = hash_make_key(h, item); memcpy(str, ((struct hash_item*)item)->str, STR_LENGTH); } fprintf(out, "\t%d:%d:%s\n", i, idx, str); } fprintf(out, "\n"); fclose(out); } #endif sslh-2.1.4/hash.h000066400000000000000000000016511463704647400136050ustar00rootroot00000000000000#ifndef HASH_H #define HASH_H /* You will need to typedef a pointer type to hash_item before including this * .h */ typedef struct hash hash; /* Function that returns a key (index) for a given item. The key must be always * the same for an item. It doesn't need to be bounded (hash.c masks it for you) */ typedef int (*hash_make_key_fn)(hash_item item); /* Function that compares two items: returns 0 if they are the same */ typedef int (*hash_cmp_item_fn)(hash_item item1, hash_item item2); hash* hash_init(int hash_size, hash_make_key_fn make_key, hash_cmp_item_fn cmp_item); int hash_insert(hash* h, hash_item new); int hash_remove(hash* h, hash_item item); /* Returns the hash item that matches specification (meaning the * comparison function returns true for cmp(x, item), or NULL if not found */ hash_item hash_find(hash* h, hash_item item); void hash_dump(hash* h, char* filename); /* For development only */ #endif sslh-2.1.4/hashtest/000077500000000000000000000000001463704647400143315ustar00rootroot00000000000000sslh-2.1.4/hashtest/Makefile000066400000000000000000000001471463704647400157730ustar00rootroot00000000000000 CFLAGS=-DHASH_TESTING -O2 -Wall OBJ=../hash.o ../gap.o htest.o htest: $(OBJ) $(CC) -o htest $(OBJ) sslh-2.1.4/hashtest/delete.tst000066400000000000000000000001001463704647400163160ustar00rootroot00000000000000# Basic delete a 10 aa a 10 ab a 10 ac a 20 ba a 21 bb d 21 bb sslh-2.1.4/hashtest/delete.tst.ref000066400000000000000000000003701463704647400171020ustar00rootroot00000000000000 0:0: 1:0: 2:0: 3:0: 4:0: 5:0: 6:0: 7:0: 8:0: 9:0: 10:10:aa 11:10:ab 12:10:ac 13:0: 14:0: 15:0: 16:0: 17:0: 18:0: 19:0: 20:20:ba 21:0: 22:0: 23:0: 24:0: 25:0: 26:0: 27:0: 28:0: 29:0: 30:0: 31:0: sslh-2.1.4/hashtest/delete_at_end.tst000066400000000000000000000001351463704647400176400ustar00rootroot00000000000000# Delete inside a block with nothing after a 10 aa a 10 ab a 12 ac a 13 ad a 14 ae d 14 ae sslh-2.1.4/hashtest/delete_at_end.tst.ref000066400000000000000000000003701463704647400204140ustar00rootroot00000000000000 0:0: 1:0: 2:0: 3:0: 4:0: 5:0: 6:0: 7:0: 8:0: 9:0: 10:10:aa 11:10:ab 12:12:ac 13:13:ad 14:0: 15:0: 16:0: 17:0: 18:0: 19:0: 20:0: 21:0: 22:0: 23:0: 24:0: 25:0: 26:0: 27:0: 28:0: 29:0: 30:0: 31:0: sslh-2.1.4/hashtest/delete_below_floor.tst000066400000000000000000000001341463704647400207160ustar00rootroot00000000000000# wrap-around and delete below floor a 2 ba a 30 aa a 30 ab a 30 ac a 30 ad a 2 bb d 30 ab sslh-2.1.4/hashtest/delete_below_floor.tst.ref000066400000000000000000000003711463704647400214740ustar00rootroot00000000000000 0:30:ad 1:0: 2:2:ba 3:2:bb 4:0: 5:0: 6:0: 7:0: 8:0: 9:0: 10:0: 11:0: 12:0: 13:0: 14:0: 15:0: 16:0: 17:0: 18:0: 19:0: 20:0: 21:0: 22:0: 23:0: 24:0: 25:0: 26:0: 27:0: 28:0: 29:0: 30:30:aa 31:30:ac sslh-2.1.4/hashtest/delete_discont.tst000066400000000000000000000001251463704647400200500ustar00rootroot00000000000000# delete in a discontinuous block a 10 aa a 11 ab a 12 ac a 14 ad a 10 bc d 11 ab sslh-2.1.4/hashtest/delete_discont.tst.ref000066400000000000000000000003701463704647400206250ustar00rootroot00000000000000 0:0: 1:0: 2:0: 3:0: 4:0: 5:0: 6:0: 7:0: 8:0: 9:0: 10:10:aa 11:10:bc 12:12:ac 13:0: 14:14:ad 15:0: 16:0: 17:0: 18:0: 19:0: 20:0: 21:0: 22:0: 23:0: 24:0: 25:0: 26:0: 27:0: 28:0: 29:0: 30:0: 31:0: sslh-2.1.4/hashtest/delete_empty.tst000066400000000000000000000001621463704647400175440ustar00rootroot00000000000000# Delete an unexisting element. And on an empty hash a 10 aa d 10 ab d 12 bc # Empty for real d 10 aa d 10 aa sslh-2.1.4/hashtest/delete_empty.tst.ref000066400000000000000000000003541463704647400203220ustar00rootroot00000000000000 0:0: 1:0: 2:0: 3:0: 4:0: 5:0: 6:0: 7:0: 8:0: 9:0: 10:0: 11:0: 12:0: 13:0: 14:0: 15:0: 16:0: 17:0: 18:0: 19:0: 20:0: 21:0: 22:0: 23:0: 24:0: 25:0: 26:0: 27:0: 28:0: 29:0: 30:0: 31:0: sslh-2.1.4/hashtest/delete_full.tst000066400000000000000000000004671463704647400173600ustar00rootroot00000000000000# delete on a full hash # First, fill the hash :-) a 0 aa a 1 ab a 2 ac a 3 ad a 4 ae a 5 af a 6 ag a 7 ah a 8 ai a 9 af a 10 ba a 11 bb a 12 bc a 13 bd a 14 be a 15 bf a 16 bg a 17 bh a 18 bi a 19 bj a 20 ca a 21 cb a 22 cd a 23 ce a 24 cf a 25 cg a 26 ch a 27 ci a 28 cj a 29 ck a 30 da a 31 db d 21 cb sslh-2.1.4/hashtest/delete_full.tst.ref000066400000000000000000000005001463704647400201170ustar00rootroot00000000000000 0:0:aa 1:1:ab 2:2:ac 3:3:ad 4:4:ae 5:5:af 6:6:ag 7:7:ah 8:8:ai 9:9:af 10:10:ba 11:11:bb 12:12:bc 13:13:bd 14:14:be 15:15:bf 16:16:bg 17:17:bh 18:18:bi 19:19:bj 20:20:ca 21:0: 22:22:cd 23:23:ce 24:24:cf 25:25:cg 26:26:ch 27:27:ci 28:28:cj 29:29:ck 30:30:da 31:31:db sslh-2.1.4/hashtest/delete_middle.tst000066400000000000000000000002101463704647400176360ustar00rootroot00000000000000# Delete inside a block with something discontinuous a 10 aa a 10 ab a 12 ac a 13 ad a 14 ae # ab shifts, ac and next doesn't d 10 aa sslh-2.1.4/hashtest/delete_middle.tst.ref000066400000000000000000000003701463704647400204200ustar00rootroot00000000000000 0:0: 1:0: 2:0: 3:0: 4:0: 5:0: 6:0: 7:0: 8:0: 9:0: 10:10:ab 11:0: 12:12:ac 13:13:ad 14:14:ae 15:0: 16:0: 17:0: 18:0: 19:0: 20:0: 21:0: 22:0: 23:0: 24:0: 25:0: 26:0: 27:0: 28:0: 29:0: 30:0: 31:0: sslh-2.1.4/hashtest/delete_wrap.tst000066400000000000000000000001461463704647400173610ustar00rootroot00000000000000# Basic delete when wrapping, between wrap and floor a 30 aa a 30 ab a 30 ac a 30 ba a 30 bb d 30 ac sslh-2.1.4/hashtest/delete_wrap.tst.ref000066400000000000000000000003701463704647400201330ustar00rootroot00000000000000 0:30:ba 1:30:bb 2:0: 3:0: 4:0: 5:0: 6:0: 7:0: 8:0: 9:0: 10:0: 11:0: 12:0: 13:0: 14:0: 15:0: 16:0: 17:0: 18:0: 19:0: 20:0: 21:0: 22:0: 23:0: 24:0: 25:0: 26:0: 27:0: 28:0: 29:0: 30:30:aa 31:30:ab sslh-2.1.4/hashtest/delete_wrap_at_end.tst000066400000000000000000000002021463704647400206640ustar00rootroot00000000000000# Delete inside a block with wrapping, with something after a 30 aa a 30 ab a 30 ac a 1 ad a 3 ae # shift ad but not ae d 14 ae sslh-2.1.4/hashtest/delete_wrap_at_end.tst.ref000066400000000000000000000003711463704647400214460ustar00rootroot00000000000000 0:30:ac 1:1:ad 2:0: 3:3:ae 4:0: 5:0: 6:0: 7:0: 8:0: 9:0: 10:0: 11:0: 12:0: 13:0: 14:0: 15:0: 16:0: 17:0: 18:0: 19:0: 20:0: 21:0: 22:0: 23:0: 24:0: 25:0: 26:0: 27:0: 28:0: 29:0: 30:30:aa 31:30:ab sslh-2.1.4/hashtest/delete_wrap_below_floor.tst000066400000000000000000000001201463704647400217420ustar00rootroot00000000000000# delete before wrap a 30 aa a 30 ab a 30 ac a 30 ad # shift ac and ad d 30 ab sslh-2.1.4/hashtest/delete_wrap_below_floor.tst.ref000066400000000000000000000003651463704647400225300ustar00rootroot00000000000000 0:30:ad 1:0: 2:0: 3:0: 4:0: 5:0: 6:0: 7:0: 8:0: 9:0: 10:0: 11:0: 12:0: 13:0: 14:0: 15:0: 16:0: 17:0: 18:0: 19:0: 20:0: 21:0: 22:0: 23:0: 24:0: 25:0: 26:0: 27:0: 28:0: 29:0: 30:30:aa 31:30:ac sslh-2.1.4/hashtest/delete_wrap_discont.tst000066400000000000000000000002121463704647400210760ustar00rootroot00000000000000# Delete with wrapping in discontinuous group a 30 aa a 30 ab a 30 ac a 31 ad a 2 ba a 3 bb # shift ac and ad but not ba and bb d 30 ab sslh-2.1.4/hashtest/delete_wrap_discont.tst.ref000066400000000000000000000003711463704647400216570ustar00rootroot00000000000000 0:31:ad 1:0: 2:2:ba 3:3:bb 4:0: 5:0: 6:0: 7:0: 8:0: 9:0: 10:0: 11:0: 12:0: 13:0: 14:0: 15:0: 16:0: 17:0: 18:0: 19:0: 20:0: 21:0: 22:0: 23:0: 24:0: 25:0: 26:0: 27:0: 28:0: 29:0: 30:30:aa 31:30:ac sslh-2.1.4/hashtest/htest000077500000000000000000000606401463704647400154140ustar00rootroot00000000000000ELF>@X@8 @#"@@@hhpp   -==-==DDPtd QtdRtd-==/lib64/ld-linux-x86-64.so.2GNUGNU}{/GٰɎem9 8H%{d O, ] l"V@libc.so.6exitfopenperrorreallocmemsetgetpagesize__isoc99_fscanffclosemallocstderrfwritefprintf__cxa_finalizestrcmp__libc_start_mainfreeGLIBC_2.7GLIBC_2.2.5_ITM_deregisterTMCloneTable__gmon_start___ITM_registerTMCloneTableii ui ==@@??? ??@@ @(@0@8@@@H@ P@ X@ `@ h@p@x@HH/HtH5/%/@%/h%/h%/h%/h%/h%/h%/h%/hp%/h`%/h P%/h @%/h 0%/h %.fAWHW AVAUATUHH56 S H(Et'H Y/'H=yH]IHEH5HHD$*IHIE1L|$AD$ HHh@L1IHH5LnPuރL$DIDH=.H5[1zD$ %d:%d:%s Usage: htest