├── .gitignore ├── GNUmakefile ├── LICENSE ├── README.md ├── addhost.sh ├── ihqu-tmpl.plx ├── ioio.pl ├── mxtx-apu.sh ├── mxtx-cp.sh ├── mxtx-gitxxdiff.pl ├── mxtx-mosh.md ├── mxtx-mosh.pl ├── mxtx-qpf-hack.pl ├── mxtx.el ├── rsh-hook.tmpl ├── src ├── ldpreload-i2usocket.c ├── ldpreload-moshclienthax.c ├── ldpreload-tcp1271conn.c ├── ldpreload-vsfa.c ├── lineread.ch ├── lpktread.ch ├── more-warnings.h ├── mxtx-dgramtunneld.c ├── mxtx-io.c ├── mxtx-lib.c ├── mxtx-lib.h ├── mxtx-rsh.c ├── mxtx-rsh.ch ├── mxtx-rshd.c ├── mxtx-socksproxy.c └── mxtx.c ├── termtower-tmpl.sh ├── tst ├── multi-strace.sh ├── test-disp-in-emacs └── test-strace-wrapper.sh └── version.sh /.gitignore: -------------------------------------------------------------------------------- 1 | # Ignore build results. Any other noise is visible. 2 | /ldpreload-i2ubind.so 3 | /ldpreload-i2uconnect5.so 4 | /ldpreload-vsfa.so 5 | /ldpreload-moshclienthax.so 6 | /ldpreload-tcp1271conn.so 7 | /libmxtx.a 8 | /mxtx 9 | /mxtx-io 10 | /mxtx-rsh 11 | /mxtx-rshd 12 | /mxtx-socksproxy 13 | mxtx-dgramtunneld 14 | -------------------------------------------------------------------------------- /GNUmakefile: -------------------------------------------------------------------------------- 1 | # 2 | # $ GNUmakefile $ 3 | # 4 | # Author: Tomi Ollila -- too ät iki piste fi 5 | # 6 | # Copyright (c) 2017 Tomi Ollila 7 | # All rights reserved 8 | # 9 | # Created: Wed 16 Aug 2017 21:09:05 EEST too 10 | # Last modified: Fri 29 Apr 2022 16:44:59 +0300 too 11 | 12 | SHELL = /bin/sh 13 | 14 | BIN := mxtx mxtx-io mxtx-rshd mxtx-rsh mxtx-socksproxy mxtx-dgramtunneld 15 | BIN += ldpreload-i2ubind.so ldpreload-i2uconnect5.so 16 | BIN += ldpreload-vsfa.so ldpreload-moshclienthax.so ldpreload-tcp1271conn.so 17 | 18 | # Note: all source file dependencies may not be listed (usually not a problem) 19 | # do `make clean all` at the end of development session... 20 | 21 | all: $(BIN) 22 | 23 | mxtx: src/mxtx.c libmxtx.a $(wildcard .git/index) 24 | sh $< 25 | 26 | mxtx-lib.o: src/mxtx-lib.c src/mxtx-lib.h 27 | sh $< 28 | 29 | libmxtx.a: src/mxtx-lib.c src/mxtx-lib.h 30 | perl -x $< 31 | 32 | mxtx-io: src/mxtx-io.c libmxtx.a 33 | sh $< 34 | 35 | mxtx-rshd: src/mxtx-rshd.c src/lpktread.ch libmxtx.a 36 | sh $< 37 | 38 | mxtx-rsh: src/mxtx-rsh.c src/lpktread.ch libmxtx.a 39 | sh $< 40 | 41 | mxtx-socksproxy: src/mxtx-socksproxy.c libmxtx.a 42 | sh $< 43 | 44 | mxtx-dgramtunneld: src/mxtx-dgramtunneld.c libmxtx.a 45 | sh $< 46 | 47 | ldpreload-i2ubind.so ldpreload-i2uconnect5.so: src/ldpreload-i2usocket.c 48 | sh $< 49 | 50 | ldpreload-%.so: src/ldpreload-%.c 51 | sh $< 52 | 53 | 54 | YES ?= NO 55 | install: $(BIN) 56 | sed '1,/^$@.sh:/d;/^#.#eos/q' GNUmakefile | /bin/sh -s YES=$(YES) $(BIN) 57 | 58 | install.sh: 59 | test -n "$1" || exit 1 # embedded shell script; not to be made directly 60 | die () { exit 1; } 61 | set -euf 62 | export LC_ALL=C LANG=C 63 | echo 64 | if test "$1" = YES=YES 65 | then mmkdir () { test -d "$1" || mkdir -vp "$1"; } 66 | mmkdir $HOME/bin/ 67 | mmkdir $HOME/.local/share/mxtx/ 68 | shift 69 | imsg=true 70 | echo strip "$@" >&2; strip "$@" 71 | xcp () { test ! -d "$3" || set -- "$1" 2 "${3%/}/${1##*/}" 72 | cmp "$1" "$3" >/dev/null 2>&1 && 73 | echo "'$1' and '$3' identical" || 74 | cp -f -v "$1" "$3"; } 75 | echo Copying: 76 | elif test "$1" = YES=DIFF # "undocumented" option... 77 | then imsg=true 78 | xcp () { 79 | test ! -d "$3" || set -- "$1" 2 "${3%/}/${1##*/}" 80 | test -e "$3" || { echo "'$3': no file"; return; } 81 | cmp "$1" "$3" >/dev/null 2>&1 || 82 | diff -u "$3" "$1" || : 83 | } 84 | echo Diffing... 85 | else 86 | imsg=false 87 | xcp () { echo '' "$@"; } 88 | echo Would install: 89 | fi 90 | xcp mxtx as $HOME/bin/.mxtx 91 | xcp ioio.pl to $HOME/bin/ 92 | xcp mxtx.el to $HOME/.local/share/mxtx/ 93 | xcp addhost.sh to $HOME/.local/share/mxtx/ 94 | xcp mxtx-rshd to $HOME/.local/share/mxtx/ 95 | xcp mxtx-io to $HOME/bin/ 96 | xcp mxtx-rsh to $HOME/bin/ 97 | xcp mxtx-cp.sh as $HOME/bin/mxtx-cp 98 | xcp mxtx-apu.sh to $HOME/bin/ 99 | xcp mxtx-mosh.pl as $HOME/bin/mxtx-mosh 100 | xcp mxtx-dgramtunneld to $HOME/.local/share/mxtx/ 101 | xcp mxtx-socksproxy as $HOME/.local/share/mxtx/socksproxy 102 | xcp ldpreload-i2ubind.so to $HOME/.local/share/mxtx/ 103 | xcp ldpreload-i2uconnect5.so to $HOME/.local/share/mxtx/ 104 | xcp ldpreload-vsfa.so to $HOME/.local/share/mxtx/ 105 | xcp ldpreload-moshclienthax.so to $HOME/.local/share/mxtx/ 106 | xcp ldpreload-tcp1271conn.so to $HOME/.local/share/mxtx/ 107 | echo 108 | $imsg || { 109 | echo Enter '' make install YES=YES '' to do so. 110 | echo 111 | } 112 | # #eos 113 | exit 1 # not reached 114 | 115 | # possibly partial uninstall 116 | unin: 117 | cd "$$HOME" && exec rm -f \ 118 | .local/share/mxtx/mxtx[.-]* \ 119 | .local/share/mxtx/ldpreload-* \ 120 | .local/share/mxtx/addhost.sh \ 121 | .local/share/mxtx/socksproxy \ 122 | ./bin/.mxtx bin/mxtx-* bin/ioio.pl 123 | 124 | .PHONY: git-head 125 | git-head: 126 | sed '1,/^$@.sh:/d;/^#.#eos/q' GNUmakefile | /bin/sh -s $@ 127 | 128 | git-head.sh: 129 | test -n "$1" || exit 1 # embedded shell script; not to be made directly 130 | die () { printf %s\\n "$*" >&2; exit 1; } 131 | set -euf 132 | # #test -z "`exec git status --porcelain -uno`" || die changes in working copy 133 | fmt='format:commit %H %h%ntree %T %t%n' 134 | fmt=$fmt'Author: %an <%ae>%nAuthorDate: %aD%n' 135 | fmt=$fmt'Commit: %cn <%ce>%nCommitDate: %cD%n%n% B' 136 | git log --pretty="$fmt" --name-status -1 > "$1" 137 | # #eos 138 | exit 1 # not reached 139 | 140 | .PHONY: dist 141 | dist: #git-head 142 | @test -z "`exec git status --porcelain -uno`" || \ 143 | echo Note: not taking changes from \'dirty\' working tree! 144 | @set -euf; set x `exec git log -1 --pretty='%h %ci'`; s=$$3-g$$2; \ 145 | set -x; exec git archive --prefix=mxtx-$$s/ -o mxtx-$$s.tar.gz HEAD 146 | 147 | # convenience helper to update mxtx over an mxtx link 148 | .PHONY: remote-update 149 | remote-update: 150 | sed '1,/^$@.sh:/d;/^#.#eos/q' GNUmakefile | /bin/sh -s "$(MAKE)" "$D" 151 | 152 | remote-update.sh: 153 | test -n "$1" || exit 1 # embedded shell script; not to be made directly 154 | die () { printf %s\\n "$*" >&2; exit 1; } 155 | set -euf 156 | export LC_ALL=C LANG=C 157 | test -n "$2" || die Usage':' $1 remote-update D=dest 158 | make=$1 D=$2 159 | set x `exec git log -1 --pretty='%h %ci'`; s=$3-g$2; 160 | die () { exit 1; } 161 | set -x 162 | test -f mxtx-$s.tar.gz || die mxtx-$s.tar.gz not made "($make a)" 163 | mxtx-rsh -n "$D" . /bin/true 164 | set +e 165 | mxtx-rsh "$D" . tar zxvf - < mxtx-$s.tar.gz 166 | mxtx-rsh -n "$D" cd mxtx-$s '&&' make install YES=YES 167 | mxtx-rsh -n "$D" . rm -rf mxtx-$s 168 | # #eos 169 | exit 1 # not reached 170 | 171 | clean: 172 | rm -rf $(BIN) mxtx-lib.o libmxtx.a git-head *~ src/*~ _tmp 173 | 174 | .SUFFIXES: 175 | MAKEFLAGS += --no-builtin-rules --warn-undefined-variables 176 | 177 | # Local variables: 178 | # mode: makefile 179 | # End: 180 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | 2 | LICENSE: 2-clause BSD license ("Simplified BSD License"): 3 | 4 | Copyright © 2017, Tomi Ollila 5 | All rights reserved. 6 | 7 | Redistribution and use in source and binary forms, with or without 8 | modification, are permitted provided that the following conditions 9 | are met: 10 | 11 | 1. Redistributions of source code must retain the above copyright 12 | notice, this list of conditions and the following disclaimer. 13 | 14 | 2. Redistributions in binary form must reproduce the above copyright 15 | notice, this list of conditions and the following disclaimer in the 16 | documentation and/or other materials provided with the distribution. 17 | 18 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 19 | AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, 20 | THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 21 | PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR 22 | CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, 23 | EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, 24 | PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR 25 | PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF 26 | LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING 27 | NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 28 | SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 29 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | 2 | ioiomxtx 3 | ======== 4 | 5 | The *ioiomxtx* software set provides `ioio.pl`, simple tool to connect 2 6 | programs together via their stdio (1,0,1,0) file descriptors. This feature 7 | yields many very powerful possibilities. 8 | 9 | The rest of the files here implements software components for multiplexing 10 | data through a software tunnel; this tunnel can be created using `ioio.pl` 11 | to connect one `mxtx` endpoint to another, often utilizing **ssh** on one 12 | (or both!) endpoints. 13 | 14 | In order to use `mxtx` this software needs to be installed on every tunnel 15 | endpoints. 16 | 17 | 18 | tl;dr; 19 | ------ 20 | 21 | ``` 22 | $ git pull --rebase --autostash 23 | $ make install YES=YES 24 | $ rm -f mxtx-20[12]*.tar.gz 25 | $ make dist 26 | $ scp mxtx-20[12]*.tar.gz rhost: 27 | $ ssh rhost 'tar zxf mxtx-20[12]*.tar.gz' 28 | $ ssh rhost 'cd mxtx-20[12]* && exec make install YES=YES' 29 | $ ssh rhost 'rm -rf mxtx-20[12]*' 30 | $ mxtx-apu.sh sshmxtx lnk rhost 31 | $ : on another terminal : 32 | $ mxtx-mosh lnk || mxtx-rsh lnk 33 | ``` 34 | 35 | 36 | ioio 37 | ---- 38 | 39 | `ioio.pl` runs two (2) commands and connects their stdio streams to each other. 40 | The implementation is very simple, effectively just around 40 lines of code. 41 | 42 | The command line usage looks like: 43 | 44 | ``` 45 | $ ioio.pl command1 [args] '///' command2 [args] 46 | ``` 47 | 48 | The string `///` is used to separate the command lines. Note that 49 | `command1` needs to write something into stdout before `command2` is 50 | executed; this is due to that the commands may ask e.g. passwords from 51 | tty before continuing -- this way the requests don't intermix (usually). 52 | 53 | With such a simple program some pretty neat things can be achieved; 54 | usually in companion with `ssh(1)`. 55 | 56 | As an alternative to `///` separator, "specially" formatted first argument 57 | can also be used as a separator (`ioio.pl` contains built-in help for more 58 | information). The following example use this alternative ('.' as a separator): 59 | 60 | ``` 61 | $ ioio.pl . ssh rhost sshfs r: mnt/rhost -o slave . /path/to/sftp-server -R 62 | ``` 63 | 64 | The above makes reverse sshfs mount -- local current directory is mounted 65 | as `mnt/rhost` on remote system, in read-only mode. Note that sftp-server 66 | has capability to access parent directories of the initial path if such 67 | thing would be an issue (e.g. using ptrace or gdb to attach running sshfs...). 68 | 69 | And: 70 | 71 | ``` 72 | $ ioio.pl ssh host1 .mxtx -c /// ssh host2 .mxtx -s 73 | ``` 74 | 75 | Accesses *host2* from *host1* using mxtx (installed as `$HOME/bin/.mxtx`) via 76 | this current host working as intermediate gateway. After ssh negotiation with 77 | *host1* is completed, the handshake message from mxtx (client) is received, 78 | and then connection to *host2* will be initiated. In this case, the order 79 | of commands could be reversed, as the mxtx server would start the handshake 80 | the same way... 81 | 82 | The above example created mxtx link `0` on client host (`host1`) for other 83 | mxtx-* commands to use. Running `.mxtx` without parameters will give good 84 | usage information for other options (better to document there than here). 85 | 86 | 87 | mxtx 88 | ---- 89 | 90 | `mxtx` is software component which creates tunnel between 2 endpoints and 91 | multiplexes traffic between these. Maximum of 250 simultaneous "connections" 92 | can be handled by this software. On one end `mxtx` is started in `client` 93 | mode, and in `server` mode on another. 94 | 95 | `mxtx` client binds unix domain socket where mxtx-aware client software can 96 | connect. Via this socket client software requests a command to be executed on 97 | server. `mxtx` client creates new "connection" and sends request to server, 98 | and bidirectional stdio between mxtx client and the program executed by server 99 | is then transferred (until EOF is received on either end). 100 | 101 | `mxtx` does not handle any flow control -- usually the socketpairs it 102 | uses to communicate with programs can take all the data coming from network 103 | socket fast enough, and if not, it waits a while to get data delivered. 104 | If endpoint is still too slow to read data (e.g. startup time took too much 105 | time), the connection will be dropped. In the case flow control is desired, 106 | the endpoints must have protocol to communicate that (so far I've managed 107 | fine without it). 108 | 109 | (That said, simple `mxtx-rsh {link} git -C {path/to/repo} log | less` can 110 | be used to trigger this "feature" (when log long enough...).) 111 | 112 | Typically `ioio.pl` is used to start `mxtx` endpoints (for the time being)... 113 | 114 | ### build c source and install 115 | 116 | ``` 117 | $ make 118 | $ make install 119 | $ make install YES=YES 120 | ``` 121 | 122 | `mxtx` is installed as `$HOME/bin/.mxtx`, to move it away from e.g. tab 123 | completion (otoh, of course, .m<TAB> completes it). It is basically a 124 | daemon program, launched by user (once) so this feels like a good name for it. 125 | 126 | Rest of user-executable programs are installed with `$HOME/bin/.mxtx-` prefix 127 | and other accombanied files at `$HOME/.local/share/mxtx/`. The command 128 | `make unin` removes most of the installed files (should remove all in 129 | `$HOME/bin/` but leaves `$HOME/.local/share/mxtx/` around). 130 | 131 | Like mentioned before `mxtx` need to be installed on all endpoints tunnels 132 | are to be created. `make dist` can be used to (git-)archive sources for 133 | bootstrap copying. 134 | 135 | ### naming and tab completion 136 | 137 | Naming is hard, and so is tab completion. After trying a few naming options 138 | (to make tab completion easier), I resorted back to (initial) `mxtx` but 139 | wanted `mx` prefix always complete to `mxtx-` -- and finally succeeded 140 | on zsh using old-style *compctl* interface. 141 | 142 | ``` 143 | function comp_cmdxpn { 144 | case $1'|'$2 145 | in mx'|') reply=(mxtx- mxtx--) 146 | ;; *) reply=() 147 | esac 148 | } 149 | compctl -C -K comp_cmdxpn + -c 150 | ``` 151 | 152 | This is good for me, but patches welcome on anything better (or bash support). 153 | 154 | ### short command introduction 155 | 156 | #### mxtx-mosh 157 | 158 | 'Mobile shell'. Tunnels the UDP traffic between mosh-clients and mosh-servers 159 | through an mxtx channel. Works pretty much like normal mosh application. 160 | Requires Mosh to be installed on mxtx endpoints. 161 | 162 | #### mxtx-rsh 163 | 164 | 'Remote' shell. Requests execution of `mxtx-rshd`. Multiplexes stdout, stderr 165 | and return value. Tracks window size (when with tty) and sends WINCH requests. 166 | 167 | #### mxtx-io 168 | 169 | Very simple `ioio` -like functionality. No additional multiplexing -- stderr 170 | of executed command is shown (on terminal) where `mxtx`s were started. 171 | 172 | #### mxtx-cp 173 | 174 | Mxtx copy command. Utilizes `rsync(1)` (subset of rsync options available, some 175 | set on default). There is also `--tar` workaround mode to environments where 176 | rsync(1) is not available (on either communication endpoints). In case there 177 | wasn't even `tar(1)` available, user could resort to `mxtx-io`, `cat(1)` and 178 | shell redirections to copy a file. 179 | 180 | #### socksproxy 181 | 182 | Special utility to tunnel traffic to designated destinations after socks5 183 | communication is completed. Binds to unix domain socket so an *ldpreload* 184 | library is used to make clients able to connect to it. There are commands 185 | `mxtx-apu.sh chromie` and `mxtx-apu.sh ffox` which start special (incognito) 186 | chrome/chromium and firefox (respectively) instance which uses this 187 | socksproxy for connections. 188 | (Note: in case of chromie, one can add `--proxy-bypass-list` command line 189 | option to silence attempt to access some sites.) 190 | 191 | `mxtx-socksproxy` is installed as `$HOME/.local/share/mxtx/socksproxy` 192 | (for now). It takes mxtx *link* names as arguments. When run it reads 193 | ($HOME/.local/share/mxtx/)`hosts-to-proxy` files on link 194 | targets to see what hosts are connectable behind every particular link 195 | ('', '/' and '.' resolve to local system -- for those connections that can 196 | be made directly by socksproxy). The tool `./addhost.sh` (installed to the 197 | same .local/share/mxtx/ directory) can be used to ease adding hosts to the 198 | `hosts-to-proxy` file. 199 | 200 | #### mxtx-apu.sh 201 | 202 | Mxtx Acute Program Usage, is a wrapper command to make some ordinary system 203 | commands use mxtx tunnels for their operations. The tool provides self-help 204 | when executed without arguments. 205 | 206 | #### mxtx.el 207 | 208 | An emacs lisp file mainly to add mxtx tramp support. `mxtx-apu.sh` has `emacs` 209 | command to ease using this file. 210 | 211 | #### ihqu-tmpl.plx 212 | 213 | Index.Html Quite Useful -- `socksproxy` request to load 214 | `$HOME/.local/share/mxtx/socksproxy/index.html` (behind link) when it is 215 | asked to forward request to `http://index-{link}.html` page. 216 | This file can be used to create such an `index.html` file (look into it). 217 | 218 | #### addhost.sh 219 | 220 | Resolves ipv4 address of a given hostname and writes results to 221 | `$HOME/.local/share/mxtx/hosts-to-proxy` (used by *socksproxy*). 222 | 223 | #### termtower-tmpl.sh 224 | 225 | `termtower-tmpl.sh`, as named as template, when executed as is, moves current 226 | graphical terminal to a new location and opens 2 more terminal windows beneath 227 | it (uxrvt, xterm (or mintty)). These terminals can be used e.g. to open `mxtx` 228 | tunnels to several hosts. Copy and edit to suit your needs... 229 | -------------------------------------------------------------------------------- /addhost.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | # 3 | # $ addhost.sh -- add host to hosts-to-proxy for socksproxy to use $ 4 | # 5 | # Author: Tomi Ollila -- too ät iki piste fi 6 | # 7 | # Copyright (c) 2017 Tomi Ollila 8 | # All rights reserved 9 | # 10 | # Created: Thu 24 Aug 2017 22:13:39 EEST too 11 | # Last modified: Mon 08 Apr 2019 20:03:21 +0300 too 12 | 13 | case ${BASH_VERSION-} in *.*) set -o posix; shopt -s xpg_echo; esac 14 | case ${ZSH_VERSION-} in *.*) emulate ksh; esac 15 | 16 | set -euf 17 | #set -x 18 | 19 | LANG=C LC_ALL=C export LANG LC_ALL; unset LANGUAGE 20 | 21 | die () { printf '%s\n' "$*"; exit 1; } >&2 22 | 23 | test $# = 1 || die "Usage: $0 host" 24 | 25 | cd $HOME/.local/share/mxtx 26 | echo Working directory: $PWD/ 27 | if grep "$1" hosts-to-proxy 2>/dev/null 28 | then die "'$1' is already in 'hosts-to-proxy file" 29 | fi 30 | # ipv4 only for now... grab first ipv4 address available 31 | am=[1-9][0-9]* 32 | host=`host -t A "$1" | sed "s/.* \($am\.$am\.$am\.$am\).*/\1/;tx;d;:x;q"` 33 | test "$host" || die "Could not figure address to host '$1'" 34 | echo "$1 $host" >> hosts-to-proxy 35 | echo "Added $1 $host to hosts-to-proxy" 36 | echo : Hint'; ' pkill -USR1 socksproxy 37 | 38 | # Local variables: 39 | # mode: shell-script 40 | # sh-basic-offset: 8 41 | # sh-indentation: 8 42 | # tab-width: 8 43 | # End: 44 | # vi: set sw=8 ts=8 45 | -------------------------------------------------------------------------------- /ihqu-tmpl.plx: -------------------------------------------------------------------------------- 1 | #!/usr/bin/perl 2 | # -*- mode: cperl; cperl-indent-level: 4 -*- 3 | 4 | # copy this file as index-src.pl, replace your links below in $USER_LINKS 5 | # and tune $TAIL (perhaps edit $HEAD), and then do $ perl index-src.pl 6 | 7 | use 5.8.1; 8 | use strict; 9 | use warnings; 10 | 11 | # note: all caps just to emphasize the parts most useful to be edited 12 | 13 | my $USER_LINKS = <<'EOF'; 14 | 15 | http://tbl1col1.example.com/ tbl1 col1 16 | http://tbl1col2.example.com/ tbl1 col2 17 | http://tbl1col3.example.com/ tbl1 col3 18 | 19 | http://tbl2col1.example.com/ tbl2 col-1 20 | http://tbl2col2.example.com/ tbl2 col-2 21 | 22 | http://tbl3col1.example.com/ tbl#3 col 1 23 | 24 | = ahdr: 25 | http://tbl4col2.example.com/ tbl4 col 2 26 | 27 | 28 | EOF 29 | 30 | my $TAIL = <<"EOF"; 31 | 32 | 33 | 41 |
42 |
43 |
http://sample-one:8080/ 45 | 46 |
47 |
48 |
https://sample-two/browse/ 50 | 51 |
52 |
53 |
54 | 55 | 56 | EOF 57 | 58 | my $HEAD = <<"EOF"; 59 | 60 | index html quite useful 61 | 64 | 74 | 75 | 76 | EOF 77 | 78 | open O, '>', 'index.wip' or die; 79 | 80 | print O $HEAD; 81 | 82 | $USER_LINKS =~ s/^\s*(.*?)\s*$/$1/s; 83 | 84 | my @rows = split /\n\n+/, $USER_LINKS; 85 | 86 | sub row($_) { 87 | foreach (split /\n/, $_[0]) { 88 | next if /^\s*#/; 89 | /^\s*(\S+)\s+(\S.*)/ or next; 90 | if ($1 eq '=') { 91 | print O qq'\n' 92 | } else { 93 | print O qq'\n' 94 | } 95 | } 96 | } 97 | row (shift @rows); 98 | foreach (@rows) { print O "
$2$2
\n"; row $_; } 99 | 100 | print O $TAIL; 101 | close O; 102 | 103 | if (-f 'index.html') { 104 | system qw/cmp -s index.wip index.html/; 105 | print("No Change\n"), unlink('index.wip'), exit unless $?; 106 | 107 | my $d = 1; 108 | my $p = ''; 109 | if (-s 'index.ar') { 110 | open A, '<', 'index.ar' or die $!; 111 | read A, $p, 64; 112 | # should check "!\n" 113 | $d = ($p =~ /index-0*(\d+)[.]html/)? $1 + 1: 1; 114 | } 115 | print "Storing previous 'index.html' (#$d) to an ar(5) archive\n"; 116 | print ": use; ar -vt index.ar ;: to see contents there\n"; 117 | open O, '>', 'index.ar.wip' or die $!; 118 | print O "!\n"; 119 | my @s = stat 'index.html'; # $s[9] = mtime, $s[7] = size 120 | printf O "index-%04d.html/%-11d 0 0 100644 %-10d\140\012", 121 | $d, $s[9], $s[7]; 122 | open I, '<', 'index.html' or die $!; 123 | my $l = read I, $_, $s[7]; 124 | die unless $l == $s[7]; 125 | close I; 126 | print O $_; 127 | print O "\n" if $s[7] & 1; 128 | if ($p) { 129 | print O substr($p, 8); 130 | print O $p while (read(A, $p, 1024*1024) > 0); 131 | close A 132 | } 133 | close O; 134 | rename 'index.ar.wip', 'index.ar'; 135 | print "...and renaming prev index.html as index.prev\n"; 136 | rename 'index.html', 'index.prev'; 137 | } 138 | 139 | rename 'index.wip', 'index.html'; 140 | -------------------------------------------------------------------------------- /ioio.pl: -------------------------------------------------------------------------------- 1 | #!/usr/bin/perl 2 | # -*- mode: cperl; cperl-indent-level: 4 -*- 3 | # $ ioio.pl $ 4 | # 5 | # Author: Tomi Ollila -- too ät iki piste fi 6 | # 7 | # Copyright (c) 2017 Tomi Ollila 8 | # All rights reserved 9 | # 10 | # Created: Thu 10 Aug 2017 23:07:28 EEST too 11 | # Last modified: Tue 12 Sep 2017 23:31:04 +0300 too 12 | 13 | use 5.8.1; 14 | use strict; 15 | use warnings; 16 | 17 | use Socket; 18 | 19 | my ($sep, $ep); 20 | if (@ARGV && $ARGV[0] =~ /(^|\/)[.]{0,2}$/) { 21 | $sep = shift @ARGV; $ep = 'other '; 22 | } else { 23 | $sep = '///'; $ep = ''; 24 | } 25 | 26 | die "\nUsage: $0 [] command1 [args] command2 [args]\n 27 | : by default '///', buf if first argument ends with '/', '/.', '/..' 28 | or is '..', '.' or '', it is used as a separator. this alternate separator 29 | cannot point to a file (is directory or empty string). it can be used to 30 | e.g. nest ioio's (also) in the first command.\n\n" unless @ARGV; 31 | 32 | my (@cmd1, @cmd2); 33 | { 34 | my $ac = 0; 35 | foreach (@ARGV) { 36 | if ($_ eq $sep) { 37 | die "'$sep' also as second arg\n" if $ac == 0; 38 | die "'$sep' as last arg\n" if $ac == $#ARGV; 39 | last; 40 | } 41 | $ac++ 42 | } 43 | die "Cannot find $ep'$sep' on command line\n" if $ac == @ARGV; 44 | @cmd1 = splice @ARGV, 0, $ac; 45 | shift; 46 | @cmd2 = @ARGV; 47 | } 48 | #print "1: @cmd1\n"; print "2: @cmd2\n"; exit; 49 | 50 | socketpair(C, P, AF_UNIX, SOCK_STREAM, PF_UNSPEC) or die "socketpair: $!"; 51 | 52 | my $pid; 53 | if ($pid = fork) { 54 | close C; 55 | open STDIN, "<&P" or die; 56 | open STDOUT, "<&P" or die; 57 | close P; 58 | exec @cmd1; 59 | die "exec: $!\n" 60 | } 61 | close P; 62 | { 63 | my $data; 64 | die "fail!?" unless defined recv C, $data, 1, MSG_PEEK; 65 | die "eof?" unless length $data; 66 | } 67 | open STDIN, "<&C" or die; 68 | open STDOUT, "<&C" or die; 69 | close C; 70 | # exec w/o fork to drop this perl -- this may result some mess in cli 71 | # after 1st process exits (and shell prompt appears) and 2nd process 72 | # writes something there (e.g Connection reset by peer). 73 | exec @cmd2; 74 | die "exec: $!\n" 75 | -------------------------------------------------------------------------------- /mxtx-apu.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | # mxtx acute program usage 4 | 5 | case ${BASH_VERSION-} in *.*) set -o posix; shopt -s xpg_echo; esac 6 | case ${ZSH_VERSION-} in *.*) emulate ksh; esac 7 | 8 | set -euf # hint: (z|ba|da|'')sh -x thisfile [args] to trace execution 9 | 10 | die () { printf '%s\n' '' "$@" ''; exit 1; } >&2 11 | 12 | x () { printf '+ %s\n' "$*" >&2; "$@"; } 13 | x_env () { printf '+ %s\n' "$*" >&2; env "$@"; } 14 | x_eval () { printf '+ %s\n' "$*" >&2; eval "$*"; } 15 | x_exec () { printf '+ %s\n' "$*" >&2; exec "$@"; exit not reached; } 16 | 17 | saved_IFS=$IFS; readonly saved_IFS 18 | 19 | usage () { printf %s\\n '' "Usage: ${0##*/} $cmd $@" ''; exit 1; } >&2 20 | 21 | #gdbrun='gdb -ex run --args' 22 | 23 | sfs_dirs=/usr/libexec/openssh:/usr/lib/openssh:/usr/lib/ssh:/usr/libexec 24 | sfs_dirs=$sfs_dirs:/usr/sbin:/usr/bin # these 2 for cygwin support 25 | 26 | if test "${MXTX_APU_WRAPPER-}" 27 | then 28 | #echo $# "$@" >&2 29 | if test "$MXTX_APU_WRAPPER" = sftp 30 | then 31 | eval r='$'$(($# - 1)) 32 | x_exec mxtx-io "$r" env PATH=$sfs_dirs sftp-server 33 | exit not reached 34 | fi 35 | if test "$MXTX_APU_WRAPPER" = xpra 36 | then exec mxtx-io "$2" /bin/sh -c "exec $3" 37 | exit not reached 38 | fi 39 | die "'$MXTX_APU_WRAPPER': unknown wrapper command" 40 | fi 41 | 42 | set_chromie () { 43 | set -- chromium chromium-freeworld chromium-browser google-chrome 44 | for chromie 45 | do command -v $chromie >/dev/null || continue 46 | case $chromie 47 | in *chrome*) cbcnfdir=g-chrome-mxtx 48 | ;; *) cbcnfdir=g-chromium-mxtx 49 | esac ; return 50 | done 51 | die 'Cannot find chrome / chromium browser on $PATH' 52 | } 53 | 54 | case $0 in */*/*) mxtxdir=$HOME/.local/share/mxtx 55 | ;; ./*) mxtxdir=$PWD 56 | ;; *) mxtxdir=$HOME/.local/share/mxtx 57 | esac 58 | 59 | _mxtxpath () { 60 | case $1 61 | in "$MXTX_PWD"/*) p=${1#"$MXTX_PWD"/} 62 | ;; "$MXTX_PWD") p= 63 | ;; *) p=$1 64 | esac 65 | printf '%s\n' "$MXTX_LINK:$p" 66 | } 67 | 68 | cmds= 69 | 70 | cmds=$cmds' 71 | cmd_path show file/dir paths -- most useful in mxtx-rsh shell' 72 | cmd_path () 73 | { 74 | if test $# = 0 75 | then 76 | printf 'pwd\n%s\n' "$PWD" 77 | rwd=`exec readlink -f "$PWD"` 78 | test "$rwd" = "$PWD" || printf '%s\n' "$rwd" 79 | test "${MXTX_PWD-}" && test "${MXTX_LINK-}" || exit 0 80 | _mxtxpath "$rwd" 81 | echo 82 | exit 83 | fi 84 | #else 85 | for arg 86 | do 87 | if test -f "$arg"; then echo f 88 | elif test -d "$arg"; then echo d 89 | else echo - 90 | fi 91 | arg=`readlink -f "$arg"` 92 | printf '%s\n' "$arg" 93 | test "${MXTX_PWD-}" && test "${MXTX_LINK-}" || continue 94 | _mxtxpath "$arg" 95 | done 96 | echo 97 | } 98 | 99 | cmds=$cmds' 100 | cmd_sshmxtx create default forward mxtx tunnel using ssh' 101 | cmd_sshmxtx () 102 | { 103 | test $# -ge 2 || usage 'link [user@]host [env=val [env...]' '' \ 104 | " 'env's, if any, will be set in host" 105 | link=$1 remote=$2 shift 2; 106 | for arg; do 107 | # perhaps too tight here... 108 | case $arg in *['(`\']*) ;; *=*) set -- "$@" \""$arg"\"; esac 109 | shift 110 | done 111 | x_exec ioio.pl / .mxtx -c"$link" / ssh "$remote" env $* .mxtx -s 112 | } 113 | 114 | cmds=$cmds' 115 | cmd_revmxtx create "reverse" mxtx tunnel' 116 | cmd_revmxtx () 117 | { 118 | test $# -ge 2 || usage 'link "back"link' 119 | x_exec ioio.pl mxtx-rsh "$1" .mxtx -c"'$2'" /// .mxtx -s~ 120 | } 121 | 122 | set_i2u_ldpra () 123 | { 124 | ldpra=$mxtxdir/ldpreload-i2uconnect5.so 125 | test -f "$ldpra" || die "'$ldpra' does not exist" 126 | export "LD_PRELOAD=$ldpra${LD_PRELOAD:+:$LD_PRELOAD}" 127 | export LD_PRELOAD 128 | 129 | } 130 | 131 | cmds=$cmds' 132 | cmd_chromie start chromium / chrome browser w/ mxtx socks5 tunneling' 133 | cmd_chromie () 134 | { 135 | case ${1-} in h) ic=--incognito 136 | ;; v) ic= 137 | ;; *) usage "('h'|'v') [link|url] ..." \ 138 | " 'h' -- incognito" " 'v' -- not" 139 | esac 140 | shift 141 | set_chromie 142 | 143 | # shortcut to http://index-{link}.html (when $1 ~ /^[a-z0-9]{1,3}$/) 144 | case ${1-} in [a-z0-9] | [a-z0-9][a-z0-9] | [a-z0-9][a-z0-9][a-z0-9] ) 145 | l=$1; shift; set -- http://index-$l.html "$@" 146 | esac 147 | set_i2u_ldpra 148 | x_exec "$chromie" --user-data-dir=$HOME/.config/$cbcnfdir \ 149 | --host-resolver-rules='MAP * 0.0.0.0 , EXCLUDE 127.1' \ 150 | $ic --proxy-server=socks5://127.1:1080 "$@" 151 | } 152 | 153 | cmds=$cmds' 154 | cmd_ffox start firefox browser w/ mxtx socks5 tunneling' 155 | cmd_ffox () 156 | { 157 | case ${1-} in h) prv=--private 158 | ;; v) prv= 159 | ;; *) usage "('h'|'v') [link|url] ..." \ 160 | " 'h' -- private" " 'v' -- not" 161 | esac 162 | shift 163 | case $HOME in *["$IFS"]*) die "Whitespace in '$HOME'"; esac 164 | profile_name=ff-mxtx 165 | profile_dir=$HOME/.config/$profile_name 166 | 167 | # shortcut to http://index-{link}.html (when $1 ~ /^[a-z0-9]{1,3}$/) 168 | case ${1-} in [a-z0-9] | [a-z0-9][a-z0-9] | [a-z0-9][a-z0-9][a-z0-9] ) 169 | l=$1; shift; set -- http://index-$l.html "$@" 170 | esac 171 | set_i2u_ldpra 172 | test -d $profile_dir || { 173 | firefox -CreateProfile "$profile_name $profile_dir" -no-remote 174 | printf %s\\n > $profile_dir/user.js \ 175 | 'user_pref("network.proxy.socks", "127.0.0.1");' \ 176 | 'user_pref("network.proxy.socks_port", 1080);' \ 177 | 'user_pref("network.proxy.socks_remote_dns", true);' \ 178 | 'user_pref("network.proxy.type", 1);' 179 | } 180 | x_exec firefox -profile $profile_dir $prv -no-remote "$@" & 181 | } 182 | 183 | cmds=$cmds' 184 | cmd_curl run curl via socks5 tunneling (mxtx socks5 proxy like 2 above)' 185 | cmd_curl () 186 | { 187 | set_i2u_ldpra 188 | x_exec curl --socks5-hostname 127.1:1080 "$@" 189 | } 190 | 191 | cmds=$cmds' 192 | cmd_aps5h run command, $all_proxy set as socks5h:... (mxtx socks5 proxy)' 193 | cmd_aps5h () 194 | { 195 | set_i2u_ldpra 196 | test $# = 0 && set -- env 197 | all_proxy=socks5h://127.0.0.1:1080 exec "$@" 198 | } 199 | 200 | find_sftp_server () { 201 | IFS=: 202 | for d in $sfs_dirs 203 | do test -x $d/sftp-server || continue 204 | IFS=$saved_IFS 205 | sftp_server=$d/sftp-server 206 | return 207 | done 208 | die "Cannot find sftp-server on local host" 209 | } 210 | 211 | cmds=$cmds' 212 | cmd_sshfs sshfs mount using mxtx tunnel' 213 | cmd_sshfs () 214 | { 215 | # versatility of sshlessfs options cannot be achieved w/ just sh code 216 | # in addition to sshfs options read-only sftp-server option would be ni 217 | test $# -ge 2 || usage 'path mountpoint [sshfs options]' '' \ 218 | " either 'path' or 'mountpoint' is to have ':' but not both."\ 219 | " 'reverse' mount (i.e. ':' in mountpoint) may have some security implications" 220 | p=$1 m=$2; shift 2 221 | case $m in *:*) 222 | case $p in *:*) die "2 link:... paths!"; esac 223 | eval la=\$$# # last arg 224 | # drop last arg 225 | a=$1; shift; for arg; do set -- "$@" "$a"; a=$arg; shift; done 226 | mp=${m#*:}; test "$mp" || mp=. 227 | case $la 228 | in '!') find_sftp_server 229 | # quite a bit of trial&error (in gitrepos.sh) to get there. 230 | x export FAKECHROOT_EXCLUDE_PATH=${sftp_server%/*}:/dev:/etc 231 | x_exec mxtx-io /// fakechroot \ 232 | /usr/bin/env /usr/sbin/chroot "$p" $sftp_server /// \ 233 | "${m%%:*}": sshfs "r:$p" "$mp" -o slave "$@" 234 | ;; '!!')find_sftp_server 235 | x_exec mxtx-io /// $sftp_server /// \ 236 | "${m%%:*}": sshfs "r:$p" "$mp" -o slave "$@" 237 | ;; *) exec >&2; echo 238 | echo Due to potential security implications to do reverse 239 | echo mount, enter either '!' or "'!!'" at the end of the 240 | echo command line. "With ! fakechroot(1) attempts to chroot" 241 | echo sftp-server to the mounted directory. With "'!!'" such 242 | echo fake chrooting is not done -- peer may ptrace '(gdb!)' 243 | echo sshfs program to cd outside of the mounted path. 244 | echo; exit 1 245 | esac 246 | esac 247 | case $p in *:*) ;; *) die "neither paths link:...!"; esac 248 | # forward mount 249 | command -v sshfs >/dev/null || die "'sshfs': no such command" 250 | x_exec mxtx-io /// sshfs "$p" "$m" -o slave "$@" /// \ 251 | "${p%%:*}": env PATH=$sfs_dirs sftp-server 252 | } 253 | 254 | cmds=$cmds' 255 | cmd_sftp like sftp, but via mxtx tunnel' 256 | cmd_sftp () 257 | { 258 | export MXTX_APU_WRAPPER=sftp 259 | x_exec sftp -S "$0" "$@" 260 | } 261 | 262 | cmds=$cmds" 263 | cmd_wgitsshc run command with GIT_SSH_COMMAND='mxtx-rsh {link} . ssh'" 264 | cmd_wgitsshc () 265 | { 266 | test $# = 0 && usage 'link (git)command [args]' 267 | l=$1; shift 268 | GIT_SSH_COMMAND="mxtx-rsh '$l' . ssh" exec "$@" 269 | } 270 | 271 | cmds=$cmds' 272 | cmd_tcp1271c tunnel localhost connection to mxtx endpoint' 273 | cmd_tcp1271c () 274 | { 275 | test $# -gt 1 || usage 'mapping command [args]' '' \ 276 | ' mapping format: port/dest[/dport][:port/dest[/dport][:...]]' \ 277 | ' example: mxtx-apu.sh tcp1271c 5901/w/5900 vncwiever :1' 278 | 279 | so=$HOME/.local/share/mxtx/ldpreload-tcp1271conn.so 280 | export LD_PRELOAD=$so${LD_PRELOAD:+:$LD_PRELOAD} 281 | export TCP1271_MAP=$1 282 | shift 283 | exec "$@" 284 | exit not reached 285 | } 286 | 287 | cmds=$cmds" 288 | cmd_emacs emacs, with loaded mxtx.el -- a bit faster if first arg is '!'" 289 | cmd_emacs () 290 | { 291 | test "${1-}" != '!' || { shift; set -- --eval '(mxtx!)' "$@"; } 292 | x_exec emacs -l $HOME/.local/share/mxtx/mxtx.el "$@" 293 | } 294 | 295 | # uncomment for xpra experiments 296 | #cmds=$cmds' 297 | #cmd_xpra xpra support (wip)' 298 | cmd_xpra () 299 | { 300 | test $# != 0 || usage 'command [link:disp] [options]' 301 | case $1 in initenv | showconfig | list ) x_exec xpra "$@" ; esac 302 | test $# -gt 1 || die "$0 xpra $1 needs display arg" 303 | xcmd=$1 disp=$2; shift 2 304 | export MXTX_APU_WRAPPER=xpra 305 | x_exec xpra --ssh="$0" "$xcmd" ssh:"$disp" "$@" 306 | } 307 | 308 | cmds=$cmds' 309 | cmd_vsfa wrap open() and stat() syscalls for opportunistic remote access' 310 | cmd_vsfa () 311 | { 312 | test $# != 0 || usage 'command [args]' 313 | so=$HOME/.local/share/mxtx/ldpreload-vsfa.so 314 | export LD_PRELOAD=$so${LD_PRELOAD:+:$LD_PRELOAD} 315 | exec "$@" 316 | } 317 | 318 | cmds=$cmds" 319 | cmd_ping 'ping' "'(including time to execute `date` on destination)' 320 | cmd_ping () 321 | { 322 | test $# = 0 && usage 'link' 323 | export TIME='elapsed: %e s' 324 | for arg 325 | do x date 326 | x /usr/bin/time mxtx-io "$arg" date 327 | done 328 | } 329 | 330 | cmds=$cmds" 331 | cmd_exit exit all .mxtx processes running on this system (dry-run w/o '!')" 332 | cmd_exit () 333 | { 334 | #pgrep -a '[.]mxtx' # -a option not in older pgreps 335 | ps x | grep '[.]mxtx' # if we also had -e, this would be unnecessary 336 | 337 | case ${1-} in '!') x_exec pkill '[.]mxtx' 338 | ;; [!.]*) x_exec pkill -f "[.]mxtx.*-c$1" 339 | esac 340 | 341 | echo "Add '!' to the command line to exit the processes shown above" 342 | } 343 | 344 | cmds=$cmds' 345 | cmd_hints hints of some more acute ways to utilize mxtx tools' 346 | cmd_hints () 347 | { 348 | printf '%s\n' '' \ 349 | "GIT_SSH_COMMAND - Use mxtx-rsh as proxy:" \ 350 | " GIT_SSH_COMMAND='mxtx-rsh {link} . ssh' git clone git@ror:{repo}" \ 351 | " GIT_SSH_COMMAND='mxtx-rsh {link} . ssh' git pull --rebase --autostash" '' \ 352 | "GIT_SSH_COMMAND - Use mxtx-rsh as replacement for ssh:" \ 353 | " GIT_SSH_COMMAND='mxtx-rsh' git push {link}:{repopath} HEAD:new-main" \ 354 | " (and on {link}: git update-ref refs/remotes/origin/main new-main)" \ 355 | " (\or no remote: git rebase new-main && git branch -d new-main)" \ 356 | " (slightly related: git update-ref refs/heads/main new-value old-value)" ''\ 357 | 'Try X11 forwarding for one (1) client:' \ 358 | " mxtx-io // socat [-x] unix-connect:/tmp/.X11-unix/X0 stdio // mxtx-rsh {link} socat unix-listen:/tmp/.X11-unix/X5 stdio" \ 359 | " (then, on {link} (on separate cli): DISPLAY=:5 xeyes" \ 360 | " it may take some time to see it -- and ~10x longer with xterm(1))" '' 361 | } 362 | 363 | #case ${1-} in --sudo) shift; exec sudo "$0" "$@"; die 'not reached'; esac 364 | 365 | case ${1-} in -x) setx=true; shift ;; *) setx=false ;; esac 366 | 367 | case ${1-} in -e) cmd=$2; readonly cmd; shift 2; cmd_"$cmd" "$@"; exit ;; esac 368 | #test "${1-}" != -e || { cmd=$2; readonly cmd; shift 2; cmd_"$cmd" "$@"; exit;} 369 | 370 | # --- 371 | 372 | IFS=' 373 | ' 374 | test $# = 0 && { 375 | echo mxtx acute program use 376 | echo 377 | echo Usage: $0 '{command} [args]' 378 | echo 379 | echo Commands of ${0##*/} "('.' to list, '.. cmd(pfx)' to view source):" 380 | echo 381 | set -- $cmds 382 | rows=$((($# + 4) / 5)) 383 | cols=$((($# + ($rows - 1)) / $rows)) 384 | c=0; while test $c -lt $rows; do eval r$c="' '"; c=$((c + 1)); done 385 | c=0; i=0 386 | for arg 387 | do arg=${arg%% *}; arg=${arg#cmd_} 388 | test $i -lt $(($# - rows)) && { 389 | arg=$arg' '; arg=${arg%${arg#???????????}}; } 390 | eval r$c='$r'$c'$arg' 391 | i=$((i + 1)); c=$((i % rows)) 392 | done 393 | c=0; while test $c -lt $rows; do eval echo \$r$c; c=$((c + 1)); done 394 | echo 395 | echo Command can be abbreviated to any unambiguous prefix. 396 | echo 397 | exit 0 398 | } 399 | cm=$1; shift 400 | 401 | case $#/$cm 402 | in 0/.) 403 | set -- $cmds 404 | IFS=' ' 405 | echo 406 | for cmd 407 | do set -- $cmd; cmd=${1#cmd_}; shift 408 | printf ' %-9s %s\n' $cmd "$*" 409 | done 410 | echo 411 | exit 412 | ;; 1/..) 413 | set +x 414 | # $1 not sanitized but that should not be too much of a problem... 415 | exec sed -n "/^cmd_$1/,/^}/p; \${g;p}" "$0" 416 | ;; */.) cm=$1; shift 417 | ;; */..) cmd=..; usage cmd-prefix 418 | 419 | #;; */d) cm=diff 420 | esac 421 | 422 | cc= cp= 423 | for m in $cmds 424 | do 425 | m=${m%% *}; m=${m#cmd_} 426 | case $m in 427 | $cm) cp= cc=1 cmd=$cm; break ;; 428 | $cm*) cp=$cc; cc=$m${cc:+, $cc}; cmd=$m 429 | esac 430 | done 431 | IFS=$saved_IFS 432 | 433 | test "$cc" || die "$0: $cm -- command not found." 434 | test "$cp" && die "$0: $cm -- ambiguous command: matches $cc" 435 | 436 | unset cc cp cm 437 | #set -x 438 | cmd'_'$cmd "$@" 439 | exit 440 | -------------------------------------------------------------------------------- /mxtx-cp.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | # -*- mode: shell-script; sh-basic-offset: 8; tab-width: 8 -*- 3 | # $ mxtx-cp.sh $ 4 | # 5 | # Author: Tomi Ollila -- too ät iki piste fi 6 | # 7 | # Copyright (c) 2017 Tomi Ollila 8 | # All rights reserved 9 | # 10 | # Created: Tue 24 Oct 2017 19:45:43 EEST too 11 | # Last modified: Tue 05 Jul 2022 16:37:24 +0300 too 12 | 13 | case ~ in '~') echo "'~' does not expand. old /bin/sh?" >&2; exit 1; esac 14 | 15 | case ${BASH_VERSION-} in *.*) set -o posix; shopt -s xpg_echo; esac 16 | case ${ZSH_VERSION-} in *.*) emulate ksh; esac 17 | 18 | set -euf 19 | 20 | LANG=C LC_ALL=C; export LANG LC_ALL; unset LANGUAGE 21 | 22 | saved_IFS=$IFS; readonly saved_IFS 23 | 24 | warn () { printf '%s\n' "$*"; } >&2 25 | die () { printf '%s\n' "$*"; exit 1; } >&2 26 | 27 | x () { printf '+ %s\n' "$*" >&2; "$@"; } 28 | x_env () { printf '+ %s\n' "$*" >&2; env "$@"; } 29 | x_eval () { printf '+ %s\n' "$*" >&2; eval "$*"; } 30 | x_exec () { printf '+ %s\n' "$*" >&2; exec "$@"; die "exec '$*' failed"; } 31 | 32 | if test $# = 0 33 | then printf >&2 %s\\n ''\ 34 | "Usage: ${0##*/} [options] [link:]src... [link:]dest" ''\ 35 | ' -[arpcsrtunxzCSHL]: as in rsync(1)'\ 36 | " -v's and -q's: one -v (with --progress) is default"\ 37 | ' --exclude=, --rsync-path=, --max-size=, --min-size=, --inplace,'\ 38 | ' --existing, --ignore-existing and '\'--\'': as in rsync(1)'\ 39 | ' --tar: use tar instead or rsync (workaround where rsync not available)'\ 40 | '' hints: \ 41 | " read rsync(1) manual page for all these options (and caveats)"\ 42 | " use '-L' to copy symlink referent (default is to skip symbolic links)"\ 43 | " use '-H' (with -a) to copy hardlinks (default is to copy file contents)"\ 44 | " use '-x' to restrict source (per entry) to one file system"\ 45 | " use '--inplace' (and retries) if copying does not complete in one go"\ 46 | " use '/' or '/.' at the end of source directory to shorten target path"\ 47 | " option '-C' (with -a/-r) is useful to filter out many vcs related files"\ 48 | " add extra quotes when spaces in filenames, like 'path/to/\"file name\"'"\ 49 | " use rsync -e mxtx-io ... for 'raw' rsync interface" '' 50 | exit 51 | fi 52 | 53 | sopts= lopts= 54 | 55 | addsopt () { case $sopts in *$1*) ;; *) sopts=$sopts$1 ;; esac; } 56 | addlopt () { case $lopts in *$1*) ;; *) lopts=$lopts\ $1${2+=$2} ;; esac; } 57 | 58 | q=0 excl= rspath= tar=false 59 | while getopts ':arpcsrtunzCSHLxqv' opt 60 | do case $opt 61 | in '?') 62 | test "$OPTARG" = - || die "'-$OPTARG': unknown ${0##*/} short option" 63 | OPTIND=2 64 | case $1 65 | in --tar) tar=true 66 | # one exclude, use 'raw' interface where more needed... 67 | ;; --exclude) excl=$2; OPTIND=3 68 | ;; --exclude=*) excl=${1#*=} 69 | ;; --rsync-path) rspath=$2; OPTIND=3 70 | ;; --rsync-path=*) rspath=${1#*=} 71 | ;; --max-size | --min-size) addlopt $1 $2; OPTIND=3 72 | ;; --max-size=* | --min-size=*) addlopt ${1%%=*} ${1#*=} 73 | ;; --existing | --ignore-existing) addlopt $1 74 | ;; --partial | --inplace) addlopt $1 # partial kept for backw. compat. 75 | ;; --one-file-system) addsopt x 76 | ;; --dry-run) addsopt n 77 | ;; --) break 78 | ;; *) die "'$1': unknown ${0##*/} long option" 79 | esac 80 | ;; a|r|p|c|s|r|t|u|n|z|C|S|H|L) addsopt $opt 81 | ;; x) sopts=$sopts$opt 82 | ;; q) q=$((q + 1)) 83 | ;; v) q=$((q - 1)) 84 | ;; *) die "'-$opt': unknown ${0##*/} short option" 85 | esac 86 | test $OPTIND = 1 || { shift $((OPTIND - 1)); OPTIND=1; } 87 | done 88 | 89 | case $q in -*) addsopt vv; addlopt --progress 90 | ;; 0) addsopt v; addlopt --progress 91 | ;; 1) 92 | ;; *) addsopt q 93 | esac 94 | 95 | $tar || { 96 | if test $# = 1 97 | then die "${0##*/}: missing destination operand after '$1'" 98 | fi 99 | case $0 in */*) c=$0 ;; *) c=./$0 ;; esac 100 | case $sopts in *v*) exec=x_exec ;; *) exec=exec ;; esac 101 | #exec=echo 102 | $exec rsync -e mxtx-rsh ${rspath:+--rsync-path="$rspath"} \ 103 | ${excl:+--exclude="$excl"} ${sopts:+-$sopts} $lopts "$@" 104 | exit not reached 105 | } 106 | 107 | test $# = 3 && test "$3" = '!' || { 108 | printf >&2 ' %s\n' '' 'With --tar:'\ 109 | " there may be one source and destination, and last argument must be '!'"\ 110 | " both source and destination may be 'remote'"\ 111 | " destination is always considered as directory (which must exist)"\ 112 | " operation will always be recursive if source is a directory"\ 113 | " in 'remote' source, extra shell whitespace/wildcard handling is done"\ 114 | " options 'p', 'z', 'q', 'n', 'x' and' '--exclude=' may work as with rsync"\ 115 | '' 116 | exit 117 | } 118 | 119 | case $sopts in *[ap]*) p=p ;; *) p= ;; esac 120 | case $sopts in *z*) z=z ;; *) z= ;; esac 121 | case $sopts in *v*) v=vv;; *) v= ;; esac 122 | case $sopts in *n*) x=t ;; *) x=x ;; esac 123 | case $sopts in *x*) ofs=--one-file-system ;; *) ofs= ;; esac 124 | 125 | case $1 in *:*) star="mxtx-rsh ${1%%:*} tar" s=${1#*:} 126 | ;; *) star=tar s=$1 127 | esac 128 | # remove '.' before 'tar' when porting for e.g. ssh use 129 | case $2 in *:*) dtar="mxtx-rsh ${2%%:*} . tar" d=${2#*:} 130 | ;; *) dtar=tar d=$2 131 | esac 132 | 133 | # cleanup trailing slashes (if any) -- and empty $s -> '.' 134 | sd= 135 | while :; do 136 | case $s in /) break 137 | ;; */) sd=/. # and to do-block 138 | ;; */.) sd=/. s=${s%.} # and to do-block 139 | ;; '') s=.; break 140 | ;; *) break 141 | esac 142 | s=${s%/} 143 | done 144 | # there is a bit of evolution here. just trailing '/' support added last... 145 | s=$s$sd 146 | case $s in *?/*) sd=${s##*/} s=${s%/*} ;; *) sd=$s s= ;; esac 147 | 148 | # trailing slashes (if any) -- and also trailing '/.'s 149 | while :; do 150 | case $d in /) break 151 | ;; */) # to do-block 152 | ;; */.) d=${d%.} # and to do-block 153 | ;; .) d=; break 154 | ;; *) break 155 | esac 156 | d=${d%/} 157 | done 158 | 159 | # note: long lines -- for easier comparison 160 | echo >&2 + \ 161 | $star ${s:+-C "$s"} $ofs ${excl:+--exclude="$excl"} -$z''cf - $sd '|' $dtar ${d:+-C "$d"} -"$z$x$v$p"f - 162 | #exit 163 | $star ${s:+-C "$s"} $ofs ${excl:+--exclude="$excl"} -$z''cf - $sd | $dtar ${d:+-C "$d"} -"$z$x$v$p"f - 164 | -------------------------------------------------------------------------------- /mxtx-gitxxdiff.pl: -------------------------------------------------------------------------------- 1 | #!/usr/bin/perl 2 | # -*- mode: cperl; cperl-indent-level: 4 -*- 3 | # $ mxtx-gitxxdiff.pl $ 4 | # 5 | # Author: Tomi Ollila -- too ät iki piste fi 6 | # 7 | # Copyright (c) 2017 Tomi Ollila 8 | # All rights reserved 9 | # 10 | # Created: Thu 07 Sep 2017 16:52:39 EEST too 11 | # Last modified: Sat 30 Dec 2017 17:40:48 +0200 too 12 | 13 | ### code-too-remote ### 14 | use 5.8.1; 15 | use strict; 16 | use warnings; 17 | ### end ### 18 | 19 | use IO::Socket::UNIX; 20 | 21 | unless (@ARGV) { 22 | my $n = (ord $0 == 47)? ( ($0 =~ /([^\/]+)$/), $1 ): $0; 23 | 24 | die "\nUsage: $n [xxdiff options and --] link:gitpath [git diff opts]\n\n"; 25 | } 26 | 27 | my @xxdiff_opts; 28 | if (ord $ARGV[0] == 45) { # '-' 29 | do { 30 | $_ = shift @ARGV; 31 | push @xxdiff_opts, $_; 32 | } while ($_ ne '--' and @ARGV); 33 | pop @xxdiff_opts; 34 | } 35 | die "Trailing '--' after xxdiff options missing.\n" unless @ARGV; 36 | 37 | my ($link, $path) = split /:/, (shift @ARGV), 2; 38 | $path = '' unless defined $path; 39 | 40 | my $s = IO::Socket::UNIX->new(Type => SOCK_STREAM, 41 | Peer => "\0/tmp/user-" . $< . "/mxtx," . $link); 42 | unless ($s) { 43 | die "Cannot connect to $link: $!\n"; 44 | } 45 | 46 | syswrite $s, "\0\0\0\005perl\0"; 47 | #syswrite $s, "\0\0\0\014strace\0perl\0"; 48 | 49 | # send code to remote perl # 50 | open I, '<', $0 or die "opening '$0':"; 51 | while () { 52 | if (/code[-]too[-]remote/) { 53 | print $s '# line ', $. + 1, "\n"; 54 | while () { 55 | last if /^### end ###\s/; 56 | print $s $_; 57 | } 58 | } 59 | } 60 | close I; 61 | print $s "remote_main\n"; 62 | print $s "__END__\n"; 63 | 64 | sysread $s, $_, 128; 65 | die "error in sending code to $link\n" unless /^code received: pid (\d+)$/; 66 | my $rpid = $1; 67 | 68 | syswrite $s, "-\n-\n::path:: $path\na:" . join("\na:", @ARGV) . "\n\n"; 69 | 70 | sysread $s, $_, 1; sysread($s, $_, 128), die "$_" unless $_ eq '0'; 71 | 72 | my @lines; 73 | while (<$s>) { 74 | last unless /^:/; 75 | chomp; 76 | push @lines, $_; 77 | } 78 | 79 | unless ($_ eq "0\n") { 80 | warn $_; 81 | alarm(2); 82 | warn $_ while (<$s>); 83 | exit 1; 84 | } 85 | 86 | die "$_" unless $_ eq "0\n"; 87 | 88 | sub run_xxdiff($$$$) 89 | { 90 | #print join(' -- ', @_), "\n"; 91 | 92 | my @xo = @xxdiff_opts; 93 | 94 | #push @xo, '--indicate-input-processed'; 95 | push @xo, '--title1', $_[1], '--title2', $_[3]; 96 | 97 | system 'xxdiff', @xo, '--', $_[0], $_[2]; 98 | #open P, '-|', 'xxdiff', @xo, '--', $_[0], $_[2]; 99 | #while (

) { unlink 'tmp1', 'tmp2'; } 100 | #close P; 101 | exit 0 if $? == 2; # ctrl-c (to xxdiff) makes it exit w/ 2 102 | } 103 | 104 | my $ldpl = $ENV{LD_PRELOAD}; 105 | if ($ldpl) { 106 | $ENV{LD_PRELOAD} = $ENV{HOME} . '/.local/share/mxtx/ldpreload-vsfa.so:' . $ldpl; 107 | } 108 | else { 109 | $ENV{LD_PRELOAD} = $ENV{HOME} . '/.local/share/mxtx/ldpreload-vsfa.so' 110 | } 111 | 112 | my $lp = $path? "$link:$path/": "$link:"; 113 | 114 | my $pnl = ''; 115 | foreach (@lines) { 116 | unless (/:\d+(\d\d\d)\s\d+(\d\d\d)\s(\w+)[.]+\s(\w+)[.]+\s(\w)\s+(.*)/) { 117 | print '.'; $pnl = "\n"; next; 118 | } 119 | print "$pnl$1 $2 $3 $4 $5 $6\n"; $pnl = ''; 120 | 121 | if ($5 ne 'M' and $5 ne 'A' and $5 ne 'D') { print "skip\n"; next; } 122 | my $lf = $3; 123 | my $rf = ($5 eq 'D' or $4 ne '000000000000')? $4: './'.$6; 124 | 125 | syswrite $s, "$lf $rf\n"; 126 | $_ = <$s>; 127 | die unless defined $_; 128 | chomp; 129 | ($lf, $rf) = split ' ', $_, 2; 130 | $lf = $lp . $lf unless ord $lf == 47; # '/'dev/null 131 | $rf = $lp . $rf unless ord $rf == 47; # '/'dev/null 132 | if ($5 eq 'M') { 133 | run_xxdiff $lf, "a:$1:$6", $rf, "b:$2:$6"; 134 | next; 135 | } 136 | if ($5 eq 'A') { 137 | run_xxdiff '/dev/null', 'added', $rf, "b:$2:$6"; 138 | next; 139 | } 140 | # 'D' 141 | run_xxdiff $lf, "a:$1:$6", '/dev/null', 'deleted'; 142 | } 143 | 144 | __END__ 145 | 146 | ### code-too-remote ### 147 | 148 | # note: `die` output goes to mxtx -s stderr 149 | 150 | my @tmps; 151 | END { unlink @tmps if @tmps; } 152 | sub openO($) 153 | { 154 | my $f = ".git/tmp-$$.$_[0]"; 155 | push @tmps, $f; 156 | open O, '>', $f or die $!; 157 | return $f; 158 | } 159 | 160 | sub write_message($$) 161 | { 162 | my $f = openO $_[0]; 163 | print O "$_[0]: $_[1]"; 164 | close O; 165 | $_[0] = $f; 166 | } 167 | sub too_large($$) 168 | { 169 | write_message $_[0], "$_[1]: larger than 524288 bytes"; 170 | } 171 | sub binary_file($) 172 | { 173 | write_message $_[0], "binary content"; 174 | } 175 | 176 | sub write_content($$) 177 | { 178 | my $buf; 179 | my $len = sysread F, $buf, 8192; 180 | die $! if ($len <= 0); 181 | if (index($buf, "\0") >= 0) { 182 | binary_file $_[0]; 183 | return; 184 | } 185 | my $f = openO $_[0]; 186 | syswrite O, $buf; 187 | my $size = $_[1] - $len; 188 | while ($size > 0) { 189 | $len = sysread F, $buf, ($size > 65536)? 65536: $size; 190 | die $! if ($len <= 0); 191 | syswrite O, $buf; 192 | $size -= $len; 193 | } 194 | close O; 195 | $_[0] = $f; 196 | } 197 | 198 | sub remote_main () 199 | { 200 | $| = 1; 201 | syswrite STDOUT, "code received: pid $$"; 202 | while () { 203 | if (/::path:: (.*)/) { 204 | if ($1 ne '.' and $1 ne '') { 205 | unless (chdir $1) { 206 | syswrite STDOUT, "failed to cd $1: $!\n"; 207 | exit 208 | } 209 | } 210 | last 211 | } 212 | } 213 | syswrite STDOUT, '0'; 214 | my @args; 215 | while () { 216 | #warn $_; 217 | next if /^a:$/; # lazy in syswrite around line 71 218 | last unless s/^a://; 219 | chomp; 220 | push @args, $_; 221 | } 222 | open(OLD, ">&", \*STDERR) or die "Can't dup STDOUT: $!"; 223 | open(STDERR, ">&", \*STDOUT) or die "Can't dup STDERR: $!"; 224 | open P, '-|', qw/git --no-pager diff --raw --abbrev=12/, @args; 225 | open(STDERR, ">&", \*OLD) or die "Can't dup OLDOUT: $!"; 226 | close OLD; 227 | undef @args; 228 | my @lines; push @lines, $_ while (

); #warn @lines; 229 | close P; 230 | exit if $?; 231 | push @lines, "0\n"; 232 | syswrite STDOUT, join('', @lines); 233 | undef @lines; 234 | while () { 235 | unlink(@tmps), @tmps = () if @tmps; 236 | chomp; 237 | my @blobs = split ' ', $_, 2; 238 | foreach (@blobs) { 239 | #warn "-- $_"; 240 | if ($_ =~ s|^[.]\/||) { 241 | my $size = -s $_; 242 | die $! unless defined $size; 243 | $_ = '/dev/null', next if $size == 0; 244 | too_large($_, $size), next if $size > 524288; 245 | binary_file($_), next if -B $_; 246 | } 247 | else { 248 | $_ = '/dev/null', next if /^000000000000/; 249 | my $size = `git cat-file -s $_`; chomp $size; 250 | too_large($_, $size), next if $size > 524288; 251 | $_ = '/dev/null', next if $size == 0; 252 | open F, '-|', qw/git cat-file -p/, $_; 253 | write_content($_, $size); 254 | close F; 255 | } 256 | } 257 | # assignments to $_ replaces @blobs list items 258 | syswrite STDOUT, "@blobs\n"; 259 | } 260 | } 261 | ### end ### 262 | -------------------------------------------------------------------------------- /mxtx-mosh.md: -------------------------------------------------------------------------------- 1 | 2 | How mosh manages its client-server connection 3 | ============================================= 4 | 5 | Normally 6 | -------- 7 | 8 | - mosh(1) perl program calls `mosh-client -c` to figure out how many colors 9 | does current terminal supports 10 | 11 | - mosh(1) uses ssh to execute `mosh-server new -c (and many other 12 | options)` to start server component. mosh-server bind inet[6] udp 13 | socket and print that along with 128bit symmetric key to stdout. mosh(1) 14 | collects that information 15 | 16 | - mosh(1) execve(2)s `mosh-client` to start sending udp datagrams to the same 17 | remote host that was used with ssh command, to the port mosh-server printed 18 | out. 19 | 20 | - when mosh-server receives datagrams, it uses the source address of those 21 | datagrams to send replies to. The source address may change -- mosh-server 22 | always replies to the address every received message came from. 23 | 24 | 25 | Through mxtx tunnel 26 | ------------------- 27 | 28 | - mxtx-mosh(1) perl program sends datagram to `mxtx-dgramtunneld` unix domain 29 | socket to ask which inet udp port is has its communication socket bound. If 30 | `mxtx-dgramtunneld` (to the particular remote in question) is not running, 31 | it attempt to start it, and then ask again. 32 | 33 | - when `mxtx-dgramtunneld` is started, it connects to the `mxtx` unix domain 34 | stream socket of the particular remote in question and requests `mxtx` to 35 | start `mxtx-dgramtunneld` at the endpoint. both endpoints send their ident 36 | messages through the `mxtx` tunnel and when these messages are accepted, 37 | local `mxtx-dgramtunneld` forks into background and parent exits with zero 38 | value. 39 | 40 | - mxtx-mosh(1) executes `mosh-client -c` to get colors like in "normal" 41 | operation 42 | 43 | - mxtx-mosh(1) uses mxtx-rsh(1) to execute `mosh-server new -c ...` 44 | pretty much like in "normal" operation. 45 | 46 | - mxtx-mosh(1) sets/adds `ldpreload-moshclienthax.so` to `LD_PRELOAD` 47 | environment variable. ldpreload-moshclienthax.so "wraps" socket(2) system 48 | call so that after it has called `socket()` it will bind(2) the just-created 49 | socket to ipv4 address `127.0..` -- . contains the port 50 | number mosh-server returned encoded in format that can be decoded by 51 | `mxtx-dgramtunneld`. the socket wrapper uses `MXTX_MOSH_PORT` environment 52 | variable to know the remote mosh-server port number. 53 | 54 | - mxtx-mosh(1) execve's `mosh-client 127.0.0.1 `. 55 | `mxtx-dgramtunneld` receives mosh-client datagrams, decodes mosh-server 56 | port number from source ip address and relays the datagram to remote 57 | `mxtx-dgramtunneld`, which in turn sends the datagram to `mosh-server`. 58 | mosh-server replies to remote mxtx-dgramtunneld, which relays reply 59 | datagram to local mxtx-dgramtunneld. local mxtx-dgramtunneld uses 60 | remote:local ports mapping table to send reply datagram to final 61 | `mosh-client` udp socket. 62 | 63 | See tl;dr; in README.md for quick mxtx-mosh test. 64 | 65 | To see on more detail how mosh (and mxtx-mosh) executes commands, enter 66 | 67 | $ strace -s256 -f -o log -e trace=execve,read mosh rhost 68 | . 69 | -------------------------------------------------------------------------------- /mxtx-mosh.pl: -------------------------------------------------------------------------------- 1 | #!/usr/bin/perl 2 | # -*- mode: cperl; cperl-indent-level: 4 -*- 3 | # $ mxtx-mosh.pl $ 4 | # 5 | # Author: Tomi Ollila -- too ät iki piste fi 6 | # 7 | # Copyright (c) 2018 Tomi Ollila 8 | # All rights reserved 9 | # 10 | # Created: Sat 03 Feb 2018 19:31:00 EET too 11 | # Last modified: Tue 19 May 2020 18:52:45 +0300 too 12 | 13 | use 5.8.1; 14 | use strict; 15 | use warnings; 16 | 17 | use IO::Socket; 18 | 19 | # the first version w/o much options 20 | 21 | die "Usage: $0 link [command [args]]\n" unless @ARGV; 22 | 23 | # mxtx-mosh(1) differs from mosh(1) when executing mosh-client as: 24 | # - escape key is set to '~' (works w/ non-us keyboards, and like openssh) 25 | # - terminal window doesn't get [mosh] prefix 26 | # - doesn't "init" terminal (--no-init in mosh(1) options) 27 | 28 | sub get_dgt_port($$) 29 | { 30 | socket my $s, AF_UNIX, SOCK_DGRAM, 0 or die 'socket: ', $!; 31 | # currently only linux abstract socket space -- portability TBD 32 | my $to = pack('Sxa*', AF_UNIX, "/tmp/user-$) { 79 | next if /^\s*$/; 80 | die "Unexpected mosh-server reply '$_'\n" unless /MOSH CONNECT (\d+) (\S+)/; 81 | ($port, $key) = ($1, $2); 82 | last; 83 | } 84 | 0 while (sysread (P, $_, 512) > 0); # strace -ff -o x ... told 341 bytes read 85 | die "No reply from mosh-server (no mosh-server?)\n" unless defined $port; 86 | close P or die $!; 87 | 88 | $ENV{ 'MOSH_KEY' } = $key; 89 | #$ENV{ 'MOSH_PREDICTION_DISPLAY' } = $predict; 90 | $ENV{ 'MOSH_NO_TERM_INIT' } = '1'; 91 | $ENV{ 'MOSH_ESCAPE_KEY' } = '~'; 92 | $ENV{ 'MOSH_TITLE_NOPREFIX' } = 't'; 93 | 94 | $ENV{ 'MXTX_MOSH_PORT' } = $port; 95 | 96 | my $ldpl = $ENV{LD_PRELOAD}; 97 | if ($ldpl) { 98 | $ENV{LD_PRELOAD} = $mxtxdir . '/ldpreload-moshclienthax.so:' . $ldpl; 99 | } 100 | else { 101 | $ENV{LD_PRELOAD} = $mxtxdir . '/ldpreload-moshclienthax.so'; 102 | } 103 | 104 | exec qw/mosh-client 127.0.0.1/, $dgt_port; 105 | -------------------------------------------------------------------------------- /mxtx-qpf-hack.pl: -------------------------------------------------------------------------------- 1 | #!/usr/bin/perl 2 | # -*- mode: cperl; cperl-indent-level: 8 -*- 3 | # $ mxtx-qpf-hack.pl $ 4 | # 5 | # Author: Tomi Ollila -- too ät iki piste fi 6 | # 7 | # Copyright (c) 2018 Tomi Ollila 8 | # All rights reserved 9 | # 10 | # Created: Fri 06 Apr 2018 18:36:35 EEST too 11 | # Last modified: Mon 07 Jun 2021 23:21:32 +0300 too 12 | 13 | # mxtx quick port forward hack 14 | # 15 | # Forward a port from local host to remote ipv4/port 16 | # at an mxtx endpoint. 17 | # with throttle (when throttle-ms not zero) wait 18 | # before writing to mtxt endpoint -- work around 19 | # some experienced unexpected exits... 20 | 21 | use 5.8.1; 22 | use strict; 23 | use warnings; 24 | 25 | $ENV{'PATH'} = '/tus/kim/pa/'; 26 | 27 | die "Usage: $0 [throttle-ms] local-port mxtx-peer remote-ip remote-port\n" 28 | unless @ARGV == 4 or @ARGV == 5; 29 | 30 | my $throttle = (@ARGV == 5)? (shift(@ARGV) / 1000): 0; 31 | 32 | use IO::Socket::INET; 33 | 34 | my $lsock = IO::Socket::INET->new(Listen => 1, 35 | LocalAddr => '127.0.0.1', 36 | LocalPort => $ARGV[0], 37 | ReuseAddr => 1, 38 | Proto => "tcp") 39 | or die "bind 127.1:$ARGV[0]: $!\n"; 40 | 41 | $lsock->listen(1) or die "listen: $!\n"; 42 | 43 | $SIG{CHLD} = 'IGNORE'; 44 | 45 | while (my $asock = $lsock->accept()) { 46 | if (fork) { 47 | close $asock; 48 | next; 49 | } 50 | close $lsock; 51 | socket S, AF_UNIX, SOCK_STREAM, 0 or die 'socket: ', $!; 52 | my $to = pack('Sxa*', AF_UNIX, "/tmp/user-$>>\n"; 77 | select undef,undef,undef, $throttle if $throttle; 78 | my $wv = syswrite(S, $buf); 79 | die "$$ <<< wrote $wv bytes -- expected $ev bytes", 80 | ' throttle (more) (for now)' unless $wv == $ev; 81 | } 82 | if (vec($rout, $pin, 1)) { 83 | my $ev = sysread S, $buf, 65536; 84 | die "$$ >>> $!\n" unless defined $ev; 85 | $tev2 += $ev; 86 | $_ = $tev2; s/(.)(...)(...)$/$1 $2 $3/; 87 | if ($ev == 0) { 88 | print STDERR "$$ >>> EOF ($_)\n"; 89 | vec($rin, $pin, 1) = 0; 90 | exit unless $rin; 91 | next 92 | } 93 | print STDERR "$$ >>> Read $ev bytes ($_) <<<\n"; 94 | my $wv = syswrite($asock, $buf); 95 | die "$$ >>> wrote $wv bytes -- expected $ev bytes", 96 | ' throttle (more) (for now)' unless $wv == $ev; 97 | } 98 | } 99 | } 100 | -------------------------------------------------------------------------------- /mxtx.el: -------------------------------------------------------------------------------- 1 | ;;; 2 | ;;; $ mxtx.el $ 3 | ;;; 4 | ;;; Author: Tomi Ollila -- too ät iki piste fi 5 | ;;; 6 | ;;; Copyright (c) 2017 Tomi Ollila 7 | ;;; All rights reserved 8 | ;;; 9 | ;;; Created: Tue 05 Sep 2017 21:50:02 EEST too 10 | ;;; Last modified: Fri 08 Dec 2017 21:33:08 +0200 too 11 | 12 | ;;; This particular file is licenced under GPL v3 (and probably later)... 13 | 14 | ;;; execute emacs -l ./mxtx.el when testing (and mxtx-apu.sh emacs works later) 15 | 16 | (require 'tramp) 17 | 18 | (add-to-list 'tramp-methods 19 | '("mxtx" ;; based on "rsync" method 20 | (tramp-login-program "mxtx-rsh") 21 | ;; executing /bin/sh is more robust than relying default (login) shell 22 | (tramp-login-args (("-t") ("%h") (".") ("/bin/sh" "-il"))) 23 | ;;(tramp-login-args (("%h"))) 24 | ;;(tramp-login-args (("-e" "none") ("%h"))) 25 | (tramp-async-args (("-q"))) 26 | (tramp-remote-shell "/bin/sh") 27 | (tramp-remote-shell-login ("-l")) 28 | (tramp-remote-shell-args ("-c")) 29 | (tramp-copy-program "rsync") 30 | (tramp-copy-args (("-t" "%k") ("-r") ("-e" "mxtx-io"))) 31 | (tramp-copy-keep-date t) 32 | ;;(tramp-copy-keep-tmpfile t) 33 | (tramp-copy-recursive t))) 34 | 35 | ;; tramp-verbose -- some hints to mxtx components debugging...? 36 | ;; (setq tramp-verbose 6) ;; M-x describe-variable ... to see levels 37 | 38 | (setq tramp-mode t 39 | tramp-copy-size-limit nil 40 | ) 41 | 42 | (defun mxtx! () 43 | "speedup tramp (a bit), modifies global env" 44 | (interactive) 45 | (require 'tramp) 46 | (setq vc-ignore-dir-regexp 47 | (format "\\(%s\\)\\|\\(%s\\)" 48 | vc-ignore-dir-regexp tramp-file-name-regexp) 49 | remote-file-name-inhibit-cache nil)) 50 | -------------------------------------------------------------------------------- /rsh-hook.tmpl: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | # -*- shell-script -*- 3 | 4 | # Copy this file as $HOME/.local/share/mxtx/rsh-hook (mode 755) 5 | # and edit according to your preferences (or do total rewrite). 6 | # This contains (example) code to change colors... 7 | 8 | set -euf 9 | 10 | # $1 = link -- currently no other possibilities supported 11 | # in the future there may be [command [args]] -- any options 12 | # (when passed here) will be informed via environment variables 13 | 14 | test $# = 1 || exit 15 | 16 | #dark=false 17 | dark=true 18 | 19 | test $dark && dl=d || dl=l 20 | 21 | _set_colors () { 22 | printf '\033]4;%d;#%s%s%s\007' 0 $1 $1 $1 23 | printf '\033]4;%d;#%s%s%s\007' 1 $2 $1 $1 24 | printf '\033]4;%d;#%s%s%s\007' 2 $1 $2 $1 25 | printf '\033]4;%d;#%s%s%s\007' 3 $3 $3 $1 26 | printf '\033]4;%d;#%s%s%s\007' 4 $1 $1 $2 27 | printf '\033]4;%d;#%s%s%s\007' 5 $3 $1 $3 28 | printf '\033]4;%d;#%s%s%s\007' 6 $1 $3 $3 29 | printf '\033]4;%d;#%s%s%s\007' 7 $4 $4 $4 30 | shift 4 31 | printf '\033]4;%d;#%s%s%s\007' 8 $1 $1 $1 32 | printf '\033]4;%d;#%s%s%s\007' 9 $2 $1 $1 33 | printf '\033]4;%d;#%s%s%s\007' 10 $1 $2 $1 34 | printf '\033]4;%d;#%s%s%s\007' 11 $3 $3 $1 35 | printf '\033]4;%d;#%s%s%s\007' 12 $1 $1 $2 36 | printf '\033]4;%d;#%s%s%s\007' 13 $3 $1 $3 37 | printf '\033]4;%d;#%s%s%s\007' 14 $1 $3 $3 38 | printf '\033]4;%d;#%s%s%s\007' 15 $4 $4 $4 39 | } 40 | # let's joke a bit... ;D 41 | darker_text_colours () { _set_colors 00 cd a0 e5 00 ee b8 f5; } 42 | lighter_text_colors () { _set_colors 00 f0 cd e5 00 ff e0 f5; } 43 | 44 | set_fg_color () { printf '\033]10;%s\007' "$1"; } 45 | set_bg_color () { printf '\033]11;%s\007' "$1"; } 46 | 47 | colorset () { 48 | set_fg_color $1 49 | set_bg_color $2 50 | ${3}er_text_colo${4-}rs 51 | } 52 | 53 | # awk '{ printf "%02x %02x %02x\t%s\n", $1,$2,$3, $0 }' /usr/share/X11/rgb.txt 54 | 55 | test "$MXTX_RSH_ENTRY" || { 56 | # exit: restore color (or set to something distinguishable) 57 | test $dark \ 58 | && colorset wheat black light \ 59 | || colorset black gray90 dark u 60 | exit 61 | } 62 | 63 | # entry: set colors based on link 64 | 65 | case $1$dl in 0[dl]) colorset lightred black light 66 | ;; 1[dl]) colorset black orchid light 67 | ;; 2[dl]) colorset black slategray3 light 68 | ;; al) colorset black '#ccbbdd' dark u 69 | ;; ad) colorset white '#223344' light 70 | ;; dl) colorset black thistle dark u 71 | ;; dd) colorset white '#482f48' light 72 | ;; z[dl]) colorset black gainsboro dark u 73 | ;; w[dl]) colorset '#80c0ff' '#111111' light 74 | esac 75 | -------------------------------------------------------------------------------- /src/ldpreload-i2usocket.c: -------------------------------------------------------------------------------- 1 | #if 0 /* -*- mode: c; c-file-style: "stroustrup"; tab-width: 8; -*- 2 | set -euf; uname=`exec uname` 3 | case $uname in *WIN*) echo $0: does not build on $uname; exit 0 ;; esac 4 | WARN="-Wall -Wstrict-prototypes -Winit-self -Wformat=2" # -pedantic 5 | WARN="$WARN -Wcast-align -Wpointer-arith " # -Wfloat-equal #-Werror 6 | WARN="$WARN -Wextra -Wwrite-strings -Wcast-qual -Wshadow" # -Wconversion 7 | WARN="$WARN -Wmissing-include-dirs -Wundef -Wbad-function-cast -Wlogical-op" 8 | WARN="$WARN -Waggregate-return -Wold-style-definition" 9 | WARN="$WARN -Wmissing-prototypes -Wmissing-declarations -Wredundant-decls" 10 | WARN="$WARN -Wnested-externs -Winline -Wvla -Woverlength-strings -Wpadded" 11 | case ${1-} in '') set x -O2; shift; esac 12 | #case ${1-} in '') set x -ggdb; shift; esac 13 | x () { printf %s\\n "$*" >&2; "$@"; } 14 | c () { 15 | trg=ldpreload-i2u''$1''$sfx.so; shift 16 | x ${CC:-gcc} -std=c99 -shared -fPIC -o "$trg" "$0" $WARN $DEFS $@ -ldl 17 | } 18 | case ${1-} in dbg) sfx=-dbg DEFS=-DDBG; shift ;; *) sfx= DEFS=-DDBG=0 ;; esac 19 | c bind -DBIND "$@" 20 | c connect5 -DCONNECT "$@" 21 | exit $? 22 | */ 23 | #endif 24 | /* 25 | * $ ldpreload-i2usocket.c $ 26 | * 27 | * Author: Tomi Ollila -- too ät iki piste fi 28 | * 29 | * Copyright (c) 2015 Tomi Ollila 30 | * All rights reserved 31 | * 32 | * Created: Tue 22 Nov 2011 16:55:43 +0200 too 33 | * For ttt: Fri 03 Oct 2014 19:21:19 +0300 too 34 | * For mxtx: Sat 26 Aug 2017 21:24:28 +0300 too 35 | * Last modified: Sat 02 Apr 2022 00:22:18 +0300 too 36 | */ 37 | 38 | #define VERDATE "1.1 (2017-08-31)" 39 | 40 | #if defined(__linux__) && __linux__ 41 | #define _GNU_SOURCE 42 | #define _DEFAULT_SOURCE 43 | #endif 44 | 45 | #include 46 | #include 47 | #include 48 | #include 49 | #include 50 | #include // for iovec 51 | #include 52 | 53 | #include 54 | #include 55 | #include 56 | 57 | #include 58 | // consts in sockaddr types problematic ... 59 | #define bind(a,b,c) xbind(a,b,c) 60 | #define connect(a,b,c) xconnect(a,b,c) 61 | #include 62 | #undef bind 63 | #undef connect 64 | #include 65 | #include 66 | #include 67 | 68 | #define null ((void*)0) 69 | 70 | #include 71 | #include 72 | 73 | #if defined(__linux__) && __linux__ 74 | #include //for older compilers not defining __BYTE_ORDER__ & friends 75 | #else 76 | #include //ditto 77 | #endif 78 | 79 | #ifndef __BYTE_ORDER 80 | #define __BYTE_ORDER _BYTE_ORDER // opportunistic, picked from freebsd 81 | #define __LITTLE_ENDIAN _LITTLE_ENDIAN 82 | #define __BIG_ENDIAN _BIG_ENDIAN 83 | #endif 84 | 85 | // gcc -dM -E -x c /dev/null | grep BYTE 86 | 87 | //#if __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__ 88 | #if __BYTE_ORDER == __BIG_ENDIAN // from endian.h 89 | 90 | #define IADDR(a,b,c,d) ((in_addr_t)((a << 24) + (b << 16) + (c << 8) + d)) 91 | #define IPORT(v) ((in_port_t)(v)) 92 | #define A256(a) ((a)<<8) 93 | 94 | //#elif __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__ 95 | #elif __BYTE_ORDER == __LITTLE_ENDIAN // from endian.h 96 | 97 | #define IADDR(a,b,c,d) ((in_addr_t)(a + (b << 8) + (c << 16) + (d << 24))) 98 | #define IPORT(v) ((in_port_t)(((v) >> 8) | ((v) << 8))) 99 | #define A256(a) (a) 100 | 101 | #else 102 | #error unknown ENDIAN 103 | #endif 104 | 105 | #if 000 106 | #if DBG 107 | static const char _vers[] = "ldpreload-" " (dbg) " VERDATE; 108 | #else 109 | static const char _vers[] = "ldpreload-" VERDATE; 110 | #endif 111 | #endif 112 | 113 | // clang -dM -E - = 4) 115 | #define ATTRIBUTE(x) __attribute__(x) 116 | #else 117 | #define ATTRIBUTE(x) 118 | #endif 119 | 120 | // (variable) block begin/end -- explicit liveness... 121 | #define BB { 122 | #define BE } 123 | 124 | #if DBG 125 | static char dbuf[4096]; 126 | #define dz do { char * dptr = dbuf; di(__LINE__) dc(':') spc 127 | #define da(a) { memcpy(dptr, a, sizeof a - 1); dptr += sizeof a - 1; } 128 | #define ds(s) if (s) { int dbgl = strlen(s); \ 129 | memcpy(dptr, s, dbgl); dptr += dbgl; } else da("(null)") 130 | #define dc(c) *dptr++ = c; 131 | #define spc *dptr++ = ' '; 132 | #define dot *dptr++ = '.'; 133 | #define cmm *dptr++ = ','; spc 134 | #define cln *dptr++ = ':'; spc 135 | #define dnl *dptr++ = '\n'; 136 | #define du(u) dptr += sprintf(dptr, "%lu", (unsigned long)i); 137 | #define di(i) dptr += sprintf(dptr, "%ld", (long)i); 138 | #define dx(x) dptr += sprintf(dptr, "%lx", (long)x); 139 | 140 | #define df da(__func__) dc('(') 141 | #define dfc dc(')') 142 | #define dss(s) da(#s) da(": \"") ds(s) dc('"') 143 | #define dsi(i) da(#i) da(": ") di(i) 144 | 145 | #define dw dnl if (write(2, dbuf, dptr - dbuf)) {} } while (0) 146 | #define dws spc if (write(2, dbuf, dptr - dbuf)) {} } while (0) 147 | 148 | #else 149 | 150 | #define DBG 0 151 | #define dz do { 152 | #define da(a) 153 | #define ds(s) 154 | #define dc(c) 155 | #define spc 156 | #define dot 157 | #define cmm 158 | #define cln 159 | #define dnl 160 | #define du(u) 161 | #define di(i) 162 | #define dx(x) 163 | 164 | #define df 165 | #define dfc 166 | #define dss(s) 167 | #define dsi(i) 168 | 169 | #define dw } while (0) 170 | #define dws } while (0) 171 | #endif 172 | #define dw0 } while (0) 173 | 174 | #if DBG 175 | static void dwritebytes(const char * info, char * p, int l) ATTRIBUTE((unused)); 176 | static void dwritebytes(const char * info, char * p, int l) 177 | { 178 | char buf[3]; 179 | int err = errno; 180 | 181 | write(2, info, strlen(info)); 182 | 183 | buf[0] = ' '; 184 | while (l--) { 185 | if (0 && *p > 32 && *p < 127) { 186 | buf[1] = *p++; 187 | write(2, buf, 2); 188 | continue; 189 | } 190 | buf[1] = hexchar[(*p>>4) & 0xf]; 191 | buf[2] = hexchar[*p++ & 0xf]; 192 | write(2, buf, 3); 193 | } 194 | write(2, "\n", 1); 195 | errno = err; 196 | } 197 | #else 198 | #define dwritebytes(i, p, l) do {} while (0) 199 | #endif 200 | 201 | 202 | static void vwarn(const char * format, va_list ap) 203 | { 204 | int error = errno; 205 | 206 | vfprintf(stderr, format, ap); 207 | if (format[strlen(format) - 1] == ':') 208 | fprintf(stderr, " %s\n", strerror(error)); 209 | else 210 | fputs("\n", stderr); 211 | fflush(stderr); 212 | } 213 | #if 0 214 | static void warn(const char * format, ...) ATTRIBUTE ((format (printf, 1, 2))); 215 | static void warn(const char * format, ...) 216 | { 217 | va_list ap; 218 | 219 | va_start(ap, format); 220 | vwarn(format, ap); 221 | va_end(ap); 222 | } 223 | #endif 224 | static void die(const char * format, ...) ATTRIBUTE ((format (printf, 1, 2))); 225 | static void die(const char * format, ...) 226 | { 227 | va_list ap; 228 | 229 | va_start(ap, format); 230 | vwarn(format, ap); 231 | va_end(ap); 232 | exit(1); 233 | } 234 | 235 | static int fill_sockaddr_un(struct sockaddr_un * addr, const char * format,...) 236 | ATTRIBUTE ((format (printf, 2, 3))); 237 | static int fill_sockaddr_un(struct sockaddr_un * addr, const char * format,...) 238 | { 239 | va_list ap; 240 | memset(addr, 0, sizeof *addr); 241 | va_start(ap, format); 242 | // we lose one char. perhaps that is tolerable 243 | int pathlen = vsnprintf(addr->sun_path, sizeof addr->sun_path, format, ap); 244 | va_end(ap); 245 | if ((unsigned)pathlen >= sizeof addr->sun_path) 246 | die("socket path length %d too big", pathlen); 247 | addr->sun_family = AF_UNIX; 248 | return pathlen + offsetof(struct sockaddr_un, sun_path); 249 | } 250 | 251 | static void * dlsym_next(const char * symbol) 252 | { 253 | void * sym = dlsym(RTLD_NEXT, symbol); 254 | char * str = dlerror(); 255 | 256 | if (str != null) 257 | die("finding symbol '%s' failed: %s", symbol, str); 258 | 259 | return sym; 260 | } 261 | #define set_next(name) *(void**)(&name##_next) = dlsym_next(#name) 262 | 263 | 264 | #ifdef BIND 265 | 266 | int bind(int sd, const struct sockaddr * addr, socklen_t addrlen); 267 | int bind(int sd, const struct sockaddr * addr, socklen_t addrlen) 268 | { 269 | static int (*bind_next)(int, const struct sockaddr *, socklen_t) = null; 270 | if (! bind_next) 271 | set_next(bind); 272 | 273 | if (addr->sa_family != AF_INET) 274 | goto _next; 275 | 276 | int socktype = -1; 277 | socklen_t typelen = sizeof socktype; 278 | (void)getsockopt(sd, SOL_SOCKET, SO_TYPE, &socktype, &typelen); 279 | if (socktype != SOCK_STREAM && socktype != SOCK_DGRAM) 280 | goto _next; 281 | 282 | in_port_t port = IPORT(((const struct sockaddr_in*)addr)->sin_port); 283 | 284 | /* fcntl flags (like non-blockingness...), probably no need to handle (?)*/ 285 | int s = socket(AF_UNIX, socktype, 0); 286 | if (s < 0) 287 | return -1; 288 | (void)dup2(s, sd); 289 | (void)close(s); 290 | 291 | s = 1; setsockopt(sd, SOL_SOCKET, SO_REUSEADDR, &s, sizeof s); 292 | 293 | struct sockaddr_un uaddr; 294 | // path format like in mxtx-socksproxy.c // xxx abstract ns now only... 295 | char c = (socktype == SOCK_STREAM)? 's': 'd'; 296 | addrlen = fill_sockaddr_un(&uaddr,"%c/tmp/user-%d/un-%c-%d", 297 | 0, getuid(), c, port); 298 | 299 | return bind_next(sd, (struct sockaddr *)&uaddr, addrlen); 300 | _next: 301 | return bind_next(sd, addr, addrlen); 302 | } 303 | 304 | #endif /* BIND */ 305 | 306 | #ifdef CONNECT 307 | 308 | /* wait up to 5000 ms ... */ 309 | static int pollthis(int fd, int event) 310 | { 311 | struct pollfd pfd; 312 | 313 | pfd.fd = fd; 314 | pfd.events = event; 315 | 316 | if (poll(&pfd, 1, 500) <= 0) /* Some EINTR protection */ 317 | if (poll(NULL, 0, 200), poll(&pfd, 1, 4300) <= 0) { 318 | dz da("poll errno: ") di(errno) dw; 319 | errno = EIO; 320 | return -1; 321 | } 322 | return 1; 323 | } 324 | 325 | int connect(int sd, const struct sockaddr * addr, socklen_t addrlen); 326 | int connect(int sd, const struct sockaddr * addr, socklen_t addrlen) 327 | { 328 | static int (*connect_next)(int, const struct sockaddr *, socklen_t) = null; 329 | if (! connect_next) 330 | set_next(connect); 331 | 332 | #if DBG 333 | if (addr->sa_family == AF_INET) { 334 | const unsigned char * s = 335 | (unsigned char *)&(((struct sockaddr_in *)addr)->sin_addr); 336 | dz da("IP: ") du(s[0]) dot du(s[1]) dot du(s[2]) dot du(s[3]) 337 | da(", PORT: ") du(IPORT(((struct sockaddr_in*)addr)->sin_port)) dw; 338 | } 339 | #endif 340 | if (addr->sa_family != AF_INET) { 341 | if (addr->sa_family == AF_INET6) return -1; // FIXME 342 | return connect_next(sd, addr, addrlen); 343 | } 344 | int socktype = -1; 345 | socklen_t typelen = sizeof socktype; 346 | (void)getsockopt(sd, SOL_SOCKET, SO_TYPE, &socktype, &typelen); 347 | if (socktype != SOCK_STREAM && socktype != SOCK_DGRAM ) 348 | //return connect_next(sd, addr, addrlen); 349 | return -1; // FIXME 350 | 351 | /* currently no AF_INET6 support ... */ 352 | int lh = 353 | ((const struct sockaddr_in*)addr)->sin_addr.s_addr == IADDR(127,0,0,1); 354 | 355 | BB; 356 | long sdflags_orig = fcntl(sd, F_GETFL); 357 | int s = socket(AF_UNIX, lh? socktype: SOCK_STREAM, 0); 358 | if (s < 0) 359 | return -1; 360 | dup2(s, sd); 361 | close(s); 362 | 363 | long sdflags_new = fcntl(sd, F_GETFL); 364 | dz da("fcntl: ") dx(sdflags_curr) dw; 365 | 366 | // nonblock or not. does not matter here // 367 | if (sdflags_new != sdflags_orig) 368 | (void)fcntl(sd, F_SETFL, sdflags_orig); 369 | 370 | #if 0 /* not needed now (or in a decade), for possible future reference */ 371 | if (flarg & O_ASYNC) { 372 | dz da("async") dw; 373 | fpid = fcntl(sd, F_GETOWN); 374 | } 375 | #endif 376 | BE; 377 | BB; 378 | struct sockaddr_un uaddr; 379 | in_port_t port = IPORT(((const struct sockaddr_in*)addr)->sin_port); 380 | 381 | char c = (! lh || socktype == SOCK_STREAM)? 's': 'd'; 382 | addrlen = fill_sockaddr_un(&uaddr,"%c/tmp/user-%d/un-%c-%d", 383 | 0, getuid(), c, port); 384 | 385 | // dz da("inet to unix port '") ds(uaddr + 1) dw; 386 | 387 | int rv = connect_next(sd, (struct sockaddr *)&uaddr, addrlen); 388 | if (lh || rv < 0) 389 | return rv; 390 | BE; 391 | 392 | /* else non-127.0.0.1 destination: socks5 communication */ 393 | 394 | (void)!write(sd, "\005\001", 3); 395 | char buf[272]; 396 | if (pollthis(sd, POLLIN) < 0) 397 | goto _nogo; 398 | if (read(sd, buf, sizeof buf) != 2) 399 | goto _nogo; 400 | if (buf[0] != 5 || buf[1] != 0) // 0: no authentication // 401 | goto _nogo; 402 | buf[0] = 5; buf[1] = (socktype == SOCK_DGRAM)? 0x03: 0x01; buf[2] = 0; 403 | buf[3] = 1; // < ATYP: ipv4 addr 404 | /* ip. 4 bytes. network byte order */ 405 | memcpy(&buf[4], &((const struct sockaddr_in*)addr)->sin_addr.s_addr, 4); 406 | /* port. 2 bytes. network byte order */ 407 | memcpy(&buf[8], &((const struct sockaddr_in*)addr)->sin_port, 2); 408 | dwritebytes("socks5 ->", buf, 10); 409 | if (pollthis(sd, POLLIN) < 0) 410 | goto _nogo; 411 | if (read(sd, buf, 5) != 5) 412 | goto _nogo; 413 | if (buf[0] != 5 || buf[1] != 0) // 0: access granted // 414 | goto _nogo; 415 | if (buf[4] == 0x01) { // ipv4 address 416 | if (read(sd, buf, 5) != 5) 417 | goto _nogo; 418 | } 419 | else if (buf[4] == 0x04) { // ipv6 address 420 | if (read(sd, buf, 17) != 17) 421 | goto _nogo; 422 | } 423 | else if (buf[4] == 0x03) { // domain name 424 | int len = (uint8_t)buf[5] + 2; 425 | if (read(sd, buf, len) != len) 426 | goto _nogo; 427 | } 428 | else 429 | goto _nogo; 430 | 431 | return 0; 432 | 433 | _nogo: 434 | shutdown(sd, 2); 435 | errno = EIO; 436 | return -1; 437 | } 438 | 439 | #endif /* CONNECT */ 440 | -------------------------------------------------------------------------------- /src/ldpreload-moshclienthax.c: -------------------------------------------------------------------------------- 1 | #if 0 /* -*- mode: c; c-file-style: "stroustrup"; tab-width: 8; -*- 2 | set -euf; uname=`exec uname` 3 | case $uname in *WIN*) echo $0: does not build on $uname; exit 0 ;; esac 4 | case ${1-} in dbg) sfx=-dbg DEFS=-DDBG; shift ;; *) sfx= DEFS=-DDBG=0 ;; esac 5 | trg=${0##*''/}; trg=${trg%.c}$sfx.so; test ! -e "$trg" || rm "$trg" 6 | case ${1-} in '') set x -O2; shift; esac 7 | #case ${1-} in '') set x -ggdb; shift; esac 8 | x_exec () { printf %s\\n "$*" >&2; exec "$@"; } 9 | x_exec ${CC:-gcc} -std=c99 -shared -fPIC -o "$trg" "$0" $DEFS $@ -ldl 10 | exit $? 11 | */ 12 | #endif 13 | /* 14 | * $ ldpreload-moshclienthax.c $ 15 | * 16 | * Author: Tomi Ollila -- too ät iki piste fi 17 | * 18 | * Copyright (c) 2017 Tomi Ollila 19 | * All rights reserved 20 | * 21 | * Created: Sat 17 Feb 2018 20:28:43 -0800 too 22 | * Last modified: Wed 14 Mar 2018 00:31:15 +0200 too 23 | */ 24 | 25 | /* when creating af_inet sock_dgram socket, bind it to 26 | 127.0.hip,lop so remote can resolve port from there 27 | env var holds that data -- mvp */ 28 | 29 | #if 0 // change to '#if 1' whenever there is desire to see these... 30 | #pragma GCC diagnostic warning "-Wpadded" 31 | #pragma GCC diagnostic warning "-Wpedantic" 32 | #endif 33 | 34 | // gcc -dM -E -xc /dev/null | grep -i gnuc 35 | // clang -dM -E -xc /dev/null | grep -i gnuc 36 | #if defined (__GNUC__) 37 | 38 | // to relax, change 'error' to 'warning' -- or even 'ignored' 39 | // selectively. use #pragma GCC diagnostic push/pop to change 40 | // the rules temporarily 41 | 42 | #pragma GCC diagnostic error "-Wall" 43 | #pragma GCC diagnostic error "-Wextra" 44 | 45 | #if __GNUC__ >= 7 46 | 47 | #pragma GCC diagnostic error "-Wimplicit-fallthrough" 48 | 49 | #endif /* __GNUC__ >= 7 */ 50 | 51 | #pragma GCC diagnostic error "-Wstrict-prototypes" 52 | #pragma GCC diagnostic error "-Winit-self" 53 | 54 | // -Wformat=2 ¡currently! (2017-12) equivalent of the following 4 55 | #pragma GCC diagnostic error "-Wformat" 56 | #pragma GCC diagnostic error "-Wformat-nonliteral" 57 | #pragma GCC diagnostic error "-Wformat-security" 58 | #pragma GCC diagnostic error "-Wformat-y2k" 59 | 60 | #pragma GCC diagnostic error "-Wcast-align" 61 | #pragma GCC diagnostic error "-Wpointer-arith" 62 | #pragma GCC diagnostic error "-Wwrite-strings" 63 | #pragma GCC diagnostic error "-Wcast-qual" 64 | #pragma GCC diagnostic error "-Wshadow" 65 | #pragma GCC diagnostic error "-Wmissing-include-dirs" 66 | #pragma GCC diagnostic error "-Wundef" 67 | #pragma GCC diagnostic error "-Wbad-function-cast" 68 | #ifndef __clang__ 69 | #pragma GCC diagnostic error "-Wlogical-op" // XXX ... 70 | #endif 71 | #pragma GCC diagnostic error "-Waggregate-return" 72 | #pragma GCC diagnostic error "-Wold-style-definition" 73 | #pragma GCC diagnostic error "-Wmissing-prototypes" 74 | #pragma GCC diagnostic error "-Wmissing-declarations" 75 | #pragma GCC diagnostic error "-Wredundant-decls" 76 | #pragma GCC diagnostic error "-Wnested-externs" 77 | #pragma GCC diagnostic error "-Winline" 78 | #pragma GCC diagnostic error "-Wvla" 79 | #pragma GCC diagnostic error "-Woverlength-strings" 80 | 81 | //ragma GCC diagnostic error "-Wfloat-equal" 82 | //ragma GCC diagnostic error "-Werror" 83 | //ragma GCC diagnostic error "-Wconversion" 84 | 85 | #endif /* defined (__GNUC__) */ 86 | 87 | #if defined(__linux__) && __linux__ 88 | #define _DEFAULT_SOURCE 89 | #define _GNU_SOURCE 90 | #endif 91 | 92 | #define socket socket_hidden 93 | 94 | #include 95 | #include 96 | 97 | #include 98 | 99 | #include 100 | #include 101 | #include 102 | 103 | #include 104 | 105 | #if defined(__linux__) && __linux__ 106 | #include //for older compilers not defining __BYTE_ORDER__ & friends 107 | #else 108 | #include //ditto 109 | #endif 110 | 111 | #undef socket 112 | 113 | #ifndef __BYTE_ORDER 114 | #define __BYTE_ORDER _BYTE_ORDER // opportunistic, picked from freebsd 115 | #define __LITTLE_ENDIAN _LITTLE_ENDIAN 116 | #define __BIG_ENDIAN _BIG_ENDIAN 117 | #endif 118 | 119 | // gcc -dM -E -x c /dev/null | grep BYTE 120 | 121 | //#if __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__ 122 | #if __BYTE_ORDER == __BIG_ENDIAN // from endian.h 123 | 124 | #define IADDR(a,b,c,d) ((in_addr_t)((a << 24) + (b << 16) + (c << 8) + d)) 125 | #define IPORT(v) ((in_port_t)(v)) 126 | #define A256(a) ((a)<<8) 127 | 128 | //#elif __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__ 129 | #elif __BYTE_ORDER == __LITTLE_ENDIAN // from endian.h 130 | 131 | #define IADDR(a,b,c,d) ((in_addr_t)(a + (b << 8) + (c << 16) + (d << 24))) 132 | #define IPORT(v) ((in_port_t)(((v) >> 8) | ((v) << 8))) 133 | #define A256(a) (a) 134 | 135 | #else 136 | #error unknown ENDIAN 137 | #endif 138 | 139 | // clang -dM -E - = 4) 141 | #define ATTRIBUTE(x) __attribute__(x) 142 | #else 143 | #define ATTRIBUTE(x) 144 | #endif 145 | 146 | // (variable) block begin/end -- explicit liveness... 147 | #define BB { 148 | #define BE } 149 | 150 | #define null ((void*)0) 151 | 152 | static void * dlsym_next(const char * symbol) 153 | { 154 | void * sym = dlsym(RTLD_NEXT, symbol); 155 | char * str = dlerror(); 156 | 157 | if (str != null) 158 | exit(77); 159 | //die("finding symbol '%s' failed: %s", symbol, str); 160 | 161 | return sym; 162 | } 163 | 164 | // Macros FTW! -- use gcc -E to examine expansion 165 | 166 | #define _deffn(_rt, _fn, _args) \ 167 | _rt _fn _args; \ 168 | _rt _fn _args { \ 169 | static _rt (*_fn##_next) _args = null; \ 170 | if (! _fn##_next ) *(void**) (&_fn##_next) = dlsym_next(#_fn); 171 | 172 | #if 0 173 | #define cprintf(...) fprintf(stderr, __VA_ARGS__) 174 | #else 175 | #define cprintf(...) do {} while (0) 176 | #endif 177 | 178 | _deffn ( int, socket, (int domain, int type, int protocol) ) 179 | #if 0 180 | { 181 | #endif 182 | int sd = socket_next(domain, type, protocol); 183 | if (sd < 0) return sd; 184 | // we know (strace(1) told us) that mosh-client uses just this socket // 185 | const char * sport; 186 | if (domain != AF_INET || type != SOCK_DGRAM 187 | || (sport = getenv("MXTX_MOSH_PORT")) == null) 188 | return sd; 189 | int iport = atoi(sport); 190 | if (iport < 1024 || iport > 65536) 191 | return sd; 192 | int hip = ((iport - 1022) / 254) + 1; 193 | int lop = ((iport - 1022) % 254) + 1; 194 | struct sockaddr_in iaddr = { 195 | .sin_family = AF_INET, 196 | .sin_addr.s_addr = IADDR(127,0,hip,lop), 197 | .sin_port = 0 198 | }; 199 | if (bind(sd, (struct sockaddr *)&iaddr, sizeof iaddr) < 0) { 200 | int e = errno; 201 | close(sd); 202 | errno = e; 203 | return -1; 204 | } 205 | return sd; 206 | } 207 | -------------------------------------------------------------------------------- /src/ldpreload-tcp1271conn.c: -------------------------------------------------------------------------------- 1 | #if 0 /* -*- mode: c; c-file-style: "stroustrup"; tab-width: 8; -*- 2 | set -euf; uname=`exec uname` 3 | case $uname in *WIN*) echo $0: does not build on $uname; exit 0 ;; esac 4 | case ${1-} in '') set x -O2; shift; esac 5 | #case ${1-} in '') set x -ggdb; shift; esac 6 | x_exec () { printf %s\\n "$*" >&2; exec "$@"; } 7 | case ${1-} in dbg) sfx=-dbg DEFS=-DDBG; shift ;; *) sfx= DEFS=-DDBG=0 ;; esac 8 | trg=${0##*''/}; trg=${trg%.c}$sfx.so; test ! -e "$trg" || rm "$trg" 9 | x_exec ${CC:-gcc} -std=c99 -shared -fPIC -o "$trg" "$0" $DEFS $@ -ldl 10 | exit $? # 11 | */ 12 | #endif 13 | /* 14 | * $ ldpreload-tcp1271conn.c $ 15 | * 16 | * Author: Tomi Ollila -- too ät iki piste fi 17 | * 18 | * Copyright (c) 2018 Tomi Ollila 19 | * All rights reserved 20 | * 21 | * Created: Thu 01 Nov 2018 21:07:27 +0200 too 22 | * Last modified: Sat 02 Apr 2022 00:22:18 +0300 too 23 | */ 24 | 25 | /* This preload library forwards requested localhost connections to a 26 | * localhost port on mxtx endpoint. The environment variable TCP1271_MAP 27 | * specifies which ports are to be redirected. 28 | * Format is: port/dest[/dport][:port/dest[/dport][:...]] 29 | * An example: LD_PRELOAD=... TCP1271_MAP=5901/w/5900 vncviewer :1 30 | * (without /dport (/5900 in example) connect to 'port' (5901 in example)). 31 | * connect(2) will fail (with EIO) if TCP1271_MAP is not defined and (with 32 | * EINVAL) if parse/port value error is encountered before match. 33 | */ 34 | 35 | #define VERDATE "1.0 (2018-11-08)" 36 | 37 | #if 0 // change to '#if 1' whenever there is desire to see these... 38 | #pragma GCC diagnostic warning "-Wpadded" 39 | #pragma GCC diagnostic warning "-Wpedantic" 40 | #endif 41 | 42 | // gcc -dM -E -xc /dev/null | grep -i gnuc 43 | // clang -dM -E -xc /dev/null | grep -i gnuc 44 | #if defined (__GNUC__) 45 | 46 | // to relax, change 'error' to 'warning' -- or even 'ignored' 47 | // selectively. use #pragma GCC diagnostic push/pop to change 48 | // the rules temporarily 49 | 50 | #pragma GCC diagnostic error "-Wall" 51 | #pragma GCC diagnostic error "-Wextra" 52 | 53 | #if __GNUC__ >= 7 54 | 55 | #pragma GCC diagnostic error "-Wimplicit-fallthrough" 56 | 57 | #endif /* __GNUC__ >= 7 */ 58 | 59 | #pragma GCC diagnostic error "-Wstrict-prototypes" 60 | #pragma GCC diagnostic error "-Winit-self" 61 | 62 | // -Wformat=2 ¡currently! (2017-12) equivalent of the following 4 63 | #pragma GCC diagnostic error "-Wformat" 64 | #pragma GCC diagnostic warning "-Wformat-nonliteral" // XXX ... 65 | #pragma GCC diagnostic error "-Wformat-security" 66 | #pragma GCC diagnostic error "-Wformat-y2k" 67 | 68 | #pragma GCC diagnostic error "-Wcast-align" 69 | #pragma GCC diagnostic error "-Wpointer-arith" 70 | #pragma GCC diagnostic error "-Wwrite-strings" 71 | #pragma GCC diagnostic error "-Wcast-qual" 72 | #pragma GCC diagnostic error "-Wshadow" 73 | #pragma GCC diagnostic error "-Wmissing-include-dirs" 74 | #pragma GCC diagnostic error "-Wundef" 75 | #pragma GCC diagnostic error "-Wbad-function-cast" 76 | #ifndef __clang__ 77 | #pragma GCC diagnostic error "-Wlogical-op" // XXX ... 78 | #endif 79 | #pragma GCC diagnostic error "-Waggregate-return" 80 | #pragma GCC diagnostic error "-Wold-style-definition" 81 | #pragma GCC diagnostic error "-Wmissing-prototypes" 82 | #pragma GCC diagnostic error "-Wmissing-declarations" 83 | #pragma GCC diagnostic error "-Wredundant-decls" 84 | #pragma GCC diagnostic error "-Wnested-externs" 85 | #pragma GCC diagnostic error "-Winline" 86 | #pragma GCC diagnostic error "-Wvla" 87 | #pragma GCC diagnostic error "-Woverlength-strings" 88 | 89 | //ragma GCC diagnostic error "-Wfloat-equal" 90 | //ragma GCC diagnostic error "-Werror" 91 | //ragma GCC diagnostic error "-Wconversion" 92 | 93 | #endif /* defined (__GNUC__) */ 94 | 95 | #if defined(__linux__) && __linux__ 96 | #define _GNU_SOURCE 97 | #define _DEFAULT_SOURCE 98 | #endif 99 | 100 | #include 101 | #include 102 | #include 103 | #include 104 | #include 105 | #include // for iovec 106 | #include 107 | 108 | #include 109 | #include 110 | #include 111 | 112 | #include 113 | // consts in sockaddr types problematic ... 114 | #define connect(a,b,c) xconnect(a,b,c) 115 | #define setsockopt(a,b,c,d,e) xsetsockopt(a,b,c,d,e) 116 | #include 117 | #undef connect 118 | #undef setsockopt 119 | #include 120 | #include 121 | #include 122 | 123 | #define null ((void*)0) 124 | 125 | #include 126 | #include // for SOL_TCP 127 | #include 128 | 129 | #if defined(__linux__) && __linux__ 130 | #include //for older compilers not defining __BYTE_ORDER__ & friends 131 | #else 132 | #include //ditto 133 | #endif 134 | 135 | #ifndef __BYTE_ORDER 136 | #define __BYTE_ORDER _BYTE_ORDER // opportunistic, picked from freebsd 137 | #define __LITTLE_ENDIAN _LITTLE_ENDIAN 138 | #define __BIG_ENDIAN _BIG_ENDIAN 139 | #endif 140 | 141 | // gcc -dM -E -x c /dev/null | grep BYTE 142 | 143 | //#if __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__ 144 | #if __BYTE_ORDER == __BIG_ENDIAN // from endian.h 145 | 146 | #define IADDR(a,b,c,d) ((in_addr_t)((a << 24) + (b << 16) + (c << 8) + d)) 147 | 148 | //#elif __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__ 149 | #elif __BYTE_ORDER == __LITTLE_ENDIAN // from endian.h 150 | 151 | #define IADDR(a,b,c,d) ((in_addr_t)(a + (b << 8) + (c << 16) + (d << 24))) 152 | 153 | #else 154 | #error unknown ENDIAN 155 | #endif 156 | 157 | #if 000 158 | #if DBG 159 | static const char _vers[] = "ldpreload-" " (dbg) " VERDATE; 160 | #else 161 | static const char _vers[] = "ldpreload-" VERDATE; 162 | #endif 163 | #endif 164 | 165 | // clang -dM -E - = 4) 167 | #define ATTRIBUTE(x) __attribute__(x) 168 | #else 169 | #define ATTRIBUTE(x) 170 | #endif 171 | 172 | // (variable) block begin/end -- explicit liveness... 173 | #define BB { 174 | #define BE } 175 | 176 | #define isizeof (int)sizeof 177 | 178 | #if DBG 179 | static char dbuf[4096]; 180 | #define dz do { char * dptr = dbuf; di(__LINE__) dc(':') spc 181 | #define da(a) { memcpy(dptr, a, sizeof a - 1); dptr += sizeof a - 1; } 182 | #define ds(s) if (s) { int dbgl = strlen(s); \ 183 | memcpy(dptr, s, dbgl); dptr += dbgl; } else da("(null)") 184 | #define dc(c) *dptr++ = c; 185 | #define spc *dptr++ = ' '; 186 | #define dot *dptr++ = '.'; 187 | #define cmm *dptr++ = ','; spc 188 | #define cln *dptr++ = ':'; spc 189 | #define dnl *dptr++ = '\n'; 190 | #define du(u) dptr += sprintf(dptr, "%lu", (unsigned long)u); 191 | #define di(i) dptr += sprintf(dptr, "%ld", (long)i); 192 | #define dx(x) dptr += sprintf(dptr, "%lx", (long)x); 193 | 194 | #define df da(__func__) dc('(') 195 | #define dfc dc(')') 196 | #define dss(s) da(#s) da(": \"") ds(s) dc('"') 197 | #define dsi(i) da(#i) da(": ") di(i) 198 | 199 | #define dw dnl if (write(2, dbuf, dptr - dbuf)) {} } while (0) 200 | #define dws spc if (write(2, dbuf, dptr - dbuf)) {} } while (0) 201 | 202 | #else 203 | 204 | #define DBG 0 205 | #define dz do { 206 | #define da(a) 207 | #define ds(s) 208 | #define dc(c) 209 | #define spc 210 | #define dot 211 | #define cmm 212 | #define cln 213 | #define dnl 214 | #define du(u) 215 | #define di(i) 216 | #define dx(x) 217 | 218 | #define df 219 | #define dfc 220 | #define dss(s) 221 | #define dsi(i) 222 | 223 | #define dw } while (0) 224 | #define dws } while (0) 225 | #endif 226 | #define dw0 } while (0) 227 | 228 | static void vwarn(const char * format, va_list ap) 229 | { 230 | int error = errno; 231 | 232 | vfprintf(stderr, format, ap); 233 | if (format[strlen(format) - 1] == ':') 234 | fprintf(stderr, " %s\n", strerror(error)); 235 | else 236 | fputs("\n", stderr); 237 | fflush(stderr); 238 | } 239 | #if 0 240 | static void warn(const char * format, ...) ATTRIBUTE ((format (printf, 1, 2))); 241 | static void warn(const char * format, ...) 242 | { 243 | va_list ap; 244 | 245 | va_start(ap, format); 246 | vwarn(format, ap); 247 | va_end(ap); 248 | } 249 | #endif 250 | static void die(const char * format, ...) ATTRIBUTE ((format (printf, 1, 2))); 251 | static void die(const char * format, ...) 252 | { 253 | va_list ap; 254 | 255 | va_start(ap, format); 256 | vwarn(format, ap); 257 | va_end(ap); 258 | exit(1); 259 | } 260 | 261 | static int fill_sockaddr_un(struct sockaddr_un * addr, const char * format,...) 262 | ATTRIBUTE ((format (printf, 2, 3))); 263 | static int fill_sockaddr_un(struct sockaddr_un * addr, const char * format,...) 264 | { 265 | va_list ap; 266 | memset(addr, 0, sizeof *addr); 267 | va_start(ap, format); 268 | // we lose one char. perhaps that is tolerable 269 | int pathlen = vsnprintf(addr->sun_path, sizeof addr->sun_path, format, ap); 270 | va_end(ap); 271 | if ((unsigned)pathlen >= sizeof addr->sun_path) 272 | die("socket path length %d too big", pathlen); 273 | addr->sun_family = AF_UNIX; 274 | return pathlen + offsetof(struct sockaddr_un, sun_path); 275 | } 276 | 277 | static void * dlsym_next(const char * symbol) 278 | { 279 | void * sym = dlsym(RTLD_NEXT, symbol); 280 | char * str = dlerror(); 281 | 282 | if (str != null) 283 | die("finding symbol '%s' failed: %s", symbol, str); 284 | 285 | return sym; 286 | } 287 | #define set_next(name) *(void**)(&name##_next) = dlsym_next(#name) 288 | 289 | 290 | // Macros FTW! -- use gcc -E to examine expansion 291 | 292 | #define _deffn(_rt, _fn, _args) \ 293 | _rt _fn _args; \ 294 | _rt _fn _args { \ 295 | static _rt (*_fn##_next) _args = null; \ 296 | if (! _fn##_next ) *(void**) (&_fn##_next) = dlsym_next(#_fn); 297 | 298 | 299 | _deffn ( int, setsockopt, (int sockfd, int level, int optname, 300 | const void *optval, socklen_t optlen) ) 301 | #if 0 302 | { 303 | #endif 304 | int rv = setsockopt_next(sockfd, level, optname, optval, optlen); 305 | if (level == SOL_TCP) return 0; 306 | return rv; 307 | } 308 | 309 | _deffn ( int, connect, (int sd, 310 | const struct sockaddr * addr, socklen_t addrlen) ) 311 | #if 0 312 | { 313 | #endif 314 | #if DBG 315 | if (addr->sa_family == AF_INET) { 316 | const unsigned char * s = 317 | (const unsigned char *)&(((struct sockaddr_in *)addr)->sin_addr); 318 | dz da("IP: ") du(s[0]) dot du(s[1]) dot du(s[2]) dot du(s[3]) 319 | da(", PORT: ") du(ntohs(((struct sockaddr_in*)addr)->sin_port)) dw; 320 | } 321 | #endif 322 | 323 | #define errno_return(e) do { errno = e; return -1; } while (0) 324 | 325 | const char * tmap = getenv("TCP1271_MAP"); 326 | if (tmap == null) errno_return(EIO); 327 | 328 | /* currently no AF_INET6 support ... */ 329 | if (addr->sa_family != AF_INET) 330 | return connect_next(sd, addr, addrlen); 331 | 332 | int socktype = -1; 333 | socklen_t typelen = sizeof socktype; 334 | (void)getsockopt(sd, SOL_SOCKET, SO_TYPE, &socktype, &typelen); 335 | 336 | /* currently no AF_INET6 support ... */ 337 | if (socktype != SOCK_STREAM) 338 | return connect_next(sd, addr, addrlen); 339 | 340 | if (((const struct sockaddr_in*)addr)->sin_addr.s_addr != IADDR(127,0,0,1)) 341 | return connect_next(sd, addr, addrlen); 342 | 343 | in_port_t dport = ntohs(((const struct sockaddr_in*)addr)->sin_port); 344 | 345 | const char * pp, *dp; 346 | int pl, dl; 347 | while (1) { 348 | char * p; 349 | int port = strtol(tmap, &p, 10); 350 | dz da("m1: ") di(port) dw; 351 | if (port < 1 || port > 65535) errno_return(EINVAL); 352 | dz da("m2: ") ds(p) dw; 353 | if (*p != '/') errno_return(EINVAL); 354 | if (port != dport) { 355 | tmap = strchr(p, ':'); 356 | if (tmap == null) return connect_next(sd, addr, addrlen); 357 | tmap++; 358 | continue; 359 | } 360 | pp = tmap; 361 | pl = p - tmap; 362 | dp = p + 1; 363 | dl = strcspn(dp, ":/ \t\n"); 364 | dz da("m3: ") di(dl) spc ds(dp) dw; 365 | const char * xp = dp + dl; 366 | if (xp[0] == ':' || xp[0] == '\0') break; 367 | if (xp[0] != '/') errno_return(EINVAL); 368 | xp++; 369 | int xport = strtol(xp, &p, 10); 370 | dz da("m4: ") di(port) dw; 371 | if (*p != ':' && *p != '\0') errno_return(EINVAL); 372 | if (xport < 1 || xport > 65535) errno_return(EINVAL); 373 | pp = xp; 374 | pl = p - xp; 375 | break; 376 | } 377 | dz da("m5: ") ds(pp) dw; 378 | while (*pp == '0') { pp++; pl--; } 379 | 380 | #define return_ECONNREFUSED do { errno = ECONNREFUSED; return -1; } while (0) 381 | 382 | BB; 383 | long sdflags_orig = fcntl(sd, F_GETFL); 384 | int s = socket(AF_UNIX, SOCK_STREAM, 0); 385 | if (s < 0) 386 | return_ECONNREFUSED; 387 | dup2(s, sd); 388 | close(s); 389 | 390 | long sdflags_new = fcntl(sd, F_GETFL); 391 | dz da("fcntl: ") dx(sdflags_new) dw; 392 | 393 | // nonblock or not. does not matter here // 394 | if (sdflags_new != sdflags_orig) 395 | (void)fcntl(sd, F_SETFL, sdflags_orig); 396 | 397 | #if 0 /* not needed now (or in a decade), for possible future reference */ 398 | if (flarg & O_ASYNC) { 399 | dz da("async") dw; 400 | fpid = fcntl(sd, F_GETOWN); 401 | } 402 | #endif 403 | BE; 404 | BB; 405 | struct sockaddr_un uaddr; 406 | 407 | addrlen = fill_sockaddr_un(&uaddr, "%c/tmp/user-%d/mxtx,%.*s", 408 | 0, getuid(), dl, dp); 409 | 410 | // dz da("inet to unix port '") ds(uaddr + 1) dw; 411 | 412 | int rv = connect_next(sd, (struct sockaddr *)&uaddr, addrlen); 413 | if (rv < 0) return rv; 414 | BE; 415 | char buf[32]; 416 | int bl = snprintf(buf + 4, sizeof buf - 4, 417 | ":connect" "%c" "127.0.0.1" "%c" "%.*s" "%c", 418 | 0, 0, pl, pp, 0); 419 | if (bl > isizeof buf - 4) die("unlikely internal error"); 420 | *((uint32_t*)&buf) = htonl(bl); 421 | (void)!write(sd, buf, bl + 4); 422 | BB; 423 | struct pollfd pfd = { .fd = sd, .events = POLLIN }; 424 | if (poll(&pfd, 1, 5000) <= 0) errno_return(EIO); 425 | BE; 426 | 427 | char b[1]; 428 | if (read(sd, b, 1) != 1) return -1; 429 | if (b[0] != 0) errno_return(EIO); 430 | return 0; 431 | } 432 | -------------------------------------------------------------------------------- /src/ldpreload-vsfa.c: -------------------------------------------------------------------------------- 1 | #if 0 /* -*- mode: c; c-file-style: "stroustrup"; tab-width: 8; -*- 2 | set -euf; uname=`exec uname` 3 | case $uname in *WIN*) echo $0: does not build on $uname; exit 0 ;; esac 4 | case ${1-} in dbg) sfx=-dbg DEFS=-DDBG; shift ;; *) sfx= DEFS=-DDBG=0 ;; esac 5 | trg=${0##*''/}; trg=${trg%.c}$sfx.so; test ! -e "$trg" || rm "$trg" 6 | case ${1-} in '') set x -O2; shift; esac 7 | #case ${1-} in '') set x -ggdb; shift; esac 8 | x_exec () { printf %s\\n "$*" >&2; exec "$@"; } 9 | x_exec ${CC:-gcc} -std=c99 -shared -fPIC -o "$trg" "$0" $DEFS $@ -ldl 10 | exit $? 11 | */ 12 | #endif 13 | /* 14 | * $ ldpreload-vsfa.c $ 15 | * 16 | * Author: Tomi Ollila -- too ät iki piste fi 17 | * 18 | * Copyright (c) 2017 Tomi Ollila 19 | * All rights reserved 20 | * 21 | * Created: Thu 14 Dec 2017 08:48:34 EET too 22 | * Last modified: Wed 14 Mar 2018 00:37:52 +0200 too 23 | */ 24 | 25 | /* "virtual soft file access" ld_preload library, a hacky module to 26 | * redirect file open to a file behind mxtx link. intercepts open() and 27 | * fopen() C library functions (and now stat(2)...). any related (like seek()) 28 | * are not supported. also, "opened" files are not seekable. thus, there are 29 | * many cases that don't work, but the point here is to focus on the cases 30 | * that do work. 31 | */ 32 | 33 | #if 0 // change to '#if 1' whenever there is desire to see these... 34 | #pragma GCC diagnostic warning "-Wpadded" 35 | #pragma GCC diagnostic warning "-Wpedantic" 36 | #endif 37 | 38 | // gcc -dM -E -xc /dev/null | grep -i gnuc 39 | // clang -dM -E -xc /dev/null | grep -i gnuc 40 | #if defined (__GNUC__) 41 | 42 | // to relax, change 'error' to 'warning' -- or even 'ignored' 43 | // selectively. use #pragma GCC diagnostic push/pop to change 44 | // the rules temporarily 45 | 46 | #pragma GCC diagnostic error "-Wall" 47 | #pragma GCC diagnostic error "-Wextra" 48 | 49 | #if __GNUC__ >= 7 50 | 51 | #pragma GCC diagnostic error "-Wimplicit-fallthrough" 52 | 53 | #endif /* __GNUC__ >= 7 */ 54 | 55 | #pragma GCC diagnostic error "-Wstrict-prototypes" 56 | #pragma GCC diagnostic error "-Winit-self" 57 | 58 | // -Wformat=2 ¡currently! (2017-12) equivalent of the following 4 59 | #pragma GCC diagnostic error "-Wformat" 60 | #pragma GCC diagnostic warning "-Wformat-nonliteral" // XXX ... 61 | #pragma GCC diagnostic error "-Wformat-security" 62 | #pragma GCC diagnostic error "-Wformat-y2k" 63 | 64 | #pragma GCC diagnostic error "-Wcast-align" 65 | #pragma GCC diagnostic error "-Wpointer-arith" 66 | #pragma GCC diagnostic error "-Wwrite-strings" 67 | #pragma GCC diagnostic error "-Wcast-qual" 68 | #pragma GCC diagnostic error "-Wshadow" 69 | #pragma GCC diagnostic error "-Wmissing-include-dirs" 70 | #pragma GCC diagnostic error "-Wundef" 71 | #pragma GCC diagnostic error "-Wbad-function-cast" 72 | #ifndef __clang__ 73 | #pragma GCC diagnostic error "-Wlogical-op" // XXX ... 74 | #endif 75 | #pragma GCC diagnostic error "-Waggregate-return" 76 | #pragma GCC diagnostic error "-Wold-style-definition" 77 | #pragma GCC diagnostic error "-Wmissing-prototypes" 78 | #pragma GCC diagnostic error "-Wmissing-declarations" 79 | #pragma GCC diagnostic error "-Wredundant-decls" 80 | #pragma GCC diagnostic error "-Wnested-externs" 81 | #pragma GCC diagnostic error "-Winline" 82 | #pragma GCC diagnostic error "-Wvla" 83 | #pragma GCC diagnostic error "-Woverlength-strings" 84 | 85 | //ragma GCC diagnostic error "-Wfloat-equal" 86 | //ragma GCC diagnostic error "-Werror" 87 | //ragma GCC diagnostic error "-Wconversion" 88 | 89 | #endif /* defined (__GNUC__) */ 90 | 91 | #if defined(__linux__) && __linux__ 92 | #define _DEFAULT_SOURCE 93 | #define _GNU_SOURCE 94 | 95 | #define _ATFILE_SOURCE 96 | #endif 97 | 98 | #define open open_hidden 99 | #define open64 open64_hidden 100 | #define openat openat_hidden 101 | #define openat64 openat64_hidden 102 | #define open open_hidden 103 | #define fopen fopen_hidden 104 | #define stat stat_x // struct stat... // 105 | #define lstat stat_hidden 106 | #define __xstat __xstat_hidden 107 | #define __xstat64 __xstat64_hidden 108 | #define __lxstat __lxstat_hidden 109 | #define __lxstat64 __lxstat64_hidden 110 | #define access access_hidden 111 | 112 | #include 113 | #include 114 | #include 115 | #include 116 | #include // offsetof() 117 | #include 118 | #include 119 | #include 120 | #include 121 | #include 122 | #include 123 | //#include <.h> 124 | 125 | #include 126 | #include 127 | 128 | #include 129 | 130 | #undef open 131 | #undef open64 132 | #undef openat 133 | #undef openat64 134 | #undef fopen 135 | #undef stat 136 | #undef lstat 137 | #undef __xstat 138 | #undef __xstat64 139 | #undef __lxstat 140 | #undef __lxstat64 141 | #undef access 142 | 143 | #define null ((void*)0) 144 | 145 | #if 000 146 | #if DBG 147 | static const char _vers[] = "ldpreload-" " (dbg) " VERDATE; 148 | #else 149 | static const char _vers[] = "ldpreload-" VERDATE; 150 | #endif 151 | #endif 152 | 153 | // clang -dM -E - = 4) 155 | #define ATTRIBUTE(x) __attribute__(x) 156 | #else 157 | #define ATTRIBUTE(x) 158 | #endif 159 | 160 | 161 | #if DBG 162 | static const char hexchar[16] = "0123456789abcdef"; 163 | static void dwritebytes(const char * info, char * p, int l) ATTRIBUTE((unused)); 164 | static void dwritebytes(const char * info, char * p, int l) 165 | { 166 | char buf[3]; 167 | int err = errno; 168 | 169 | write(2, info, strlen(info)); 170 | 171 | buf[0] = ' '; 172 | while (l--) { 173 | if (0 && *p > 32 && *p < 127) { 174 | buf[1] = *p++; 175 | write(2, buf, 2); 176 | continue; 177 | } 178 | buf[1] = hexchar[(*p>>4) & 0xf]; 179 | buf[2] = hexchar[*p++ & 0xf]; 180 | write(2, buf, 3); 181 | } 182 | write(2, "\n", 1); 183 | errno = err; 184 | } 185 | #else 186 | #define dwritebytes(i, p, l) do {} while (0) 187 | #endif 188 | 189 | 190 | static void vwarn(const char * format, va_list ap) 191 | { 192 | int error = errno; 193 | 194 | vfprintf(stderr, format, ap); 195 | if (format[strlen(format) - 1] == ':') 196 | fprintf(stderr, " %s\n", strerror(error)); 197 | else 198 | fputs("\n", stderr); 199 | fflush(stderr); 200 | } 201 | static void warn(const char * format, ...) ATTRIBUTE ((format (printf, 1, 2))); 202 | static void warn(const char * format, ...) 203 | { 204 | va_list ap; 205 | 206 | va_start(ap, format); 207 | vwarn(format, ap); 208 | va_end(ap); 209 | } 210 | static void die(const char * format, ...) ATTRIBUTE ((format (printf, 1, 2))); 211 | static void die(const char * format, ...) 212 | { 213 | va_list ap; 214 | 215 | va_start(ap, format); 216 | vwarn(format, ap); 217 | va_end(ap); 218 | exit(1); 219 | } 220 | 221 | static void * dlsym_next(const char * symbol) 222 | { 223 | void * sym = dlsym(RTLD_NEXT, symbol); 224 | char * str = dlerror(); 225 | 226 | if (str != null) 227 | die("finding symbol '%s' failed: %s", symbol, str); 228 | 229 | return sym; 230 | } 231 | 232 | 233 | // from mxtx-lib.c... 234 | static bool checkpeerid(int sd) 235 | { 236 | #if defined(__linux__) && __linux__ || defined(__CYGWIN__) && __CYGWIN__ 237 | struct ucred cr; 238 | socklen_t len = sizeof cr; 239 | if (getsockopt(sd, SOL_SOCKET, SO_PEERCRED, &cr, &len) < 0) { 240 | warn("getsockopt SO_PEERCRED on channel %d failed:", sd); 241 | return false; 242 | } 243 | int uid = (int)getuid(); 244 | if ((int)cr.uid != uid) { 245 | warn("Peer real uid %d not %d on channel %d", (int)cr.uid, uid, sd); 246 | return false; 247 | } 248 | return true; 249 | #elif defined (__XXXunsupp__) && __XXXunsupp__ 250 | #error fixme, when known what and how... 251 | //getpeerucred(...); 252 | warn("Peer check unimplemented"); 253 | return false; 254 | #else 255 | // fallback default -- so far not tested anywhere // 256 | int peuid, pegid; 257 | if (getpeereid(sd, &peuid, &pegid) < 0) { 258 | warn("getpeereid on channel %d failed:", sd); 259 | return false; 260 | } 261 | int euid = (int)geteuid(); 262 | if (peuid != euid) { 263 | warn("Peer effective uid %d not %d on channel %d",(int)peuid,euid, sd); 264 | return false; 265 | } 266 | return true; 267 | #endif 268 | } 269 | 270 | // slightly different than connect_to_mxtx(default_mxtx_socket_path(lnk)); 271 | // perhaps unify, PIC to be solved first, though (and parallelism in that other 272 | static int connect_to_mxtx_with(const char * lnk) 273 | { 274 | struct sockaddr_un addr = { 275 | .sun_family = AF_UNIX 276 | }; 277 | // default_mxtx_socket_path() part 278 | #if defined(__linux__) && __linux__ 279 | uid_t uid = getuid(); 280 | int len = snprintf(addr.sun_path, sizeof addr.sun_path, 281 | "@/tmp/user-%d/mxtx,%s", uid, lnk); 282 | addr.sun_path[0] = '\0'; 283 | #else 284 | char * xdgrd = getenv("XDG_RUNTIME_DIR"); 285 | int len; 286 | if (xdgrd) { 287 | len = snprintf(addr.sun_path, sizeof addr.sun_path, 288 | "%s/mxtx,%s", xdgrd, lnk); 289 | } 290 | else { 291 | uid_t uid = getuid(); 292 | (void)snprintf(addr.sun_path, 293 | sizeof addr.sun_path, "/tmp/user-%d", uid); 294 | (void)mkdir(addr.sun_path, 0700); 295 | len = snprintf(addr.sun_path, 296 | sizeof addr.sun_path, "/tmp/user-%d/mxtx,%s", uid, lnk); 297 | } 298 | #endif 299 | if ((size_t)len >= sizeof addr.sun_path) { 300 | warn("socket path too long (%d octets)", len); 301 | return -1; 302 | } 303 | 304 | int sd = socket(AF_UNIX, SOCK_STREAM, 0); 305 | if (sd < 0) { 306 | warn("creating socket failed:"); 307 | return -1; 308 | } 309 | int one = 1; 310 | setsockopt(sd, SOL_SOCKET, SO_KEEPALIVE, &one, sizeof one); 311 | 312 | int pathlen = len + offsetof(struct sockaddr_un, sun_path); 313 | if (connect(sd, (struct sockaddr *)&addr, pathlen) < 0) { 314 | if (addr.sun_path[0] == '\0') addr.sun_path[0] = '@'; 315 | warn("connecting to mxtx port %s failed:", addr.sun_path); 316 | goto closerr; 317 | } 318 | if (! checkpeerid(sd)) 319 | goto closerr; 320 | 321 | return sd; 322 | closerr: 323 | close(sd); 324 | return -1; 325 | } 326 | 327 | 328 | #define LNKMAX 32 329 | static unsigned getlink(char * lnk, const char * pathname) 330 | { 331 | for (int i = 0; pathname[i]; i++) { 332 | if (pathname[i] == ':') { 333 | if (i >= LNKMAX) break; 334 | memcpy(lnk, pathname, i); 335 | lnk[i] = '\0'; 336 | return i + 1; 337 | } 338 | if (pathname[i] == '/') break; 339 | } 340 | return 0; 341 | } 342 | 343 | #pragma GCC diagnostic push 344 | #pragma GCC diagnostic ignored "-Wunused-result" // write(2) calls // 345 | 346 | static int open_read(const char * lnk, const char * pathname) 347 | { 348 | // w/ use of gather array (and writev) some copying and memory 349 | // usage could be avoided, but it works surprisingly non-atomically) 350 | char buf[4120]; 351 | int plen = strlen(pathname) + 1; 352 | if (plen > 4096) return -1; 353 | 354 | int sd = connect_to_mxtx_with(lnk); 355 | int mlen = plen + 4; 356 | buf[0] = buf[1] = 0; buf[2] = mlen >> 8; buf[3] = mlen & 0xff; 357 | /*buf[4] = '~';*/ buf[4] = 'c'; buf[5] = 'a'; buf[6] = 't'; buf[7] = '\0'; 358 | memcpy(buf + 8, pathname, plen); 359 | write(sd, buf, mlen + 4); 360 | return sd; 361 | } 362 | 363 | static const char sh[] = "set -eu" 364 | "\n" "if test \"$0\" = '+'; then exec 3>>\"$1\"; else exec 3>\"$1\"; fi" 365 | "\n" "test -z \"$2\" || chmod $2 \"$1\" || :" 366 | "\n" "exec cat >&3"; 367 | 368 | static int open_write(const char * lnk, const char * pathname, 369 | int append, int mode) 370 | { 371 | //write(2, sh, sizeof sh); 372 | char buf[6144]; 373 | int plen = strlen(pathname) + 1; 374 | if (plen > 4096) return -1; 375 | int sd = connect_to_mxtx_with(lnk); 376 | buf[0] = buf[1] = 0; /**/ buf[4] = 's'; buf[5] = 'h'; buf[6] = '\0'; 377 | buf[7] = '-'; buf[8] = 'c'; buf[9] = '\0'; 378 | memcpy(buf + 10, sh, sizeof sh); 379 | char * p = buf + 10 + sizeof sh; 380 | *p++ = append? '+': '-'; *p++ = '\0'; 381 | memcpy(p, pathname, plen); p += plen; 382 | if ((unsigned)mode <= 0777) { 383 | sprintf(p, "%o", mode); 384 | p += strlen(p); 385 | } 386 | else *p = '\0'; 387 | int mlen = p - buf - 3; 388 | buf[2] = mlen >> 8; buf[3] = mlen & 0xff; 389 | write(sd, buf, mlen + 4); 390 | return sd; 391 | } 392 | 393 | static int stat_it(const char * lnk, const char * pathname, struct stat_x * st) 394 | { 395 | char buf[4120]; 396 | int plen = strlen(pathname) + 1; 397 | if (plen > 4096) return -1; 398 | 399 | int sd = connect_to_mxtx_with(lnk); 400 | 401 | #define STAT_CMD "stat\0-c\0%d %i %f %u %g %s %B %b %X %Y %Z" 402 | memcpy(buf + 4, STAT_CMD, sizeof STAT_CMD); 403 | char * p = buf + 4 + sizeof STAT_CMD; 404 | #undef STAT_CMD 405 | 406 | memcpy(p, pathname, plen); p += plen; 407 | int mlen = p - buf - 4; 408 | 409 | buf[0] = buf[1] = 0; buf[2] = mlen >> 8; buf[3] = mlen & 0xff; 410 | write(sd, buf, mlen + 4); 411 | int l = read(sd, buf, 512); 412 | close(sd); 413 | if (l < 20) return -1; 414 | buf[l] = '\0'; 415 | st->st_dev = strtol(buf, &p, 10); 416 | st->st_ino = strtol(p, &p, 10); 417 | st->st_mode = strtol(p, &p, 16); 418 | st->st_nlink = 1; 419 | st->st_uid = strtol(p, &p, 10); 420 | st->st_gid = strtol(p, &p, 10); 421 | st->st_rdev = 0; 422 | st->st_size = strtol(p, &p, 10); 423 | st->st_blksize = strtol(p, &p, 10); 424 | st->st_blocks = strtol(p, &p, 10); 425 | st->st_atime = strtol(p, &p, 10); 426 | st->st_mtime = strtol(p, &p, 10); 427 | st->st_ctime = strtol(p, null, 10); 428 | return 0; 429 | } 430 | #pragma GCC diagnostic pop 431 | 432 | // Macros FTW! -- use gcc -E to examine expansion 433 | 434 | #define _deffn(_rt, _fn, _args) \ 435 | _rt _fn _args; \ 436 | _rt _fn _args { \ 437 | static _rt (*_fn##_next) _args = null; \ 438 | if (! _fn##_next ) *(void**) (&_fn##_next) = dlsym_next(#_fn); 439 | 440 | #if 0 441 | #define cprintf(...) fprintf(stderr, __VA_ARGS__) 442 | #else 443 | #define cprintf(...) do {} while (0) 444 | #endif 445 | 446 | _deffn ( int, open, (const char * pathname, int flags, mode_t mode) ) 447 | #if 0 448 | { 449 | #endif 450 | cprintf("*** open(\"%s\", %x, %o)\n", pathname, flags, mode); 451 | char lnk[LNKMAX]; 452 | int o; 453 | if ((o = getlink(lnk, pathname)) == 0) 454 | return open_next(pathname, flags, mode); 455 | 456 | if (flags & O_RDWR) return -1; 457 | 458 | if (flags & O_WRONLY) 459 | return open_write(lnk, pathname + o, (flags & O_APPEND), mode & 0777); 460 | else 461 | return open_read(lnk, pathname + o); 462 | } 463 | 464 | _deffn ( int, open64, (const char * pathname, int flags, mode_t mode) ) 465 | #if 0 466 | { 467 | #endif 468 | cprintf("*** open64(\"%s\", %x, %o)\n", pathname, flags, mode); 469 | char lnk[LNKMAX]; 470 | int o; 471 | if ((o = getlink(lnk, pathname)) == 0) 472 | return open64_next(pathname, flags, mode); 473 | 474 | if (flags & O_RDWR) return -1; 475 | 476 | if (flags & O_WRONLY) 477 | return open_write(lnk, pathname + o, (flags & O_APPEND), mode & 0777); 478 | else 479 | return open_read(lnk, pathname + o); 480 | } 481 | 482 | _deffn ( int, openat, (int dirfd, const char * pathname, int flags, mode_t mode) ) 483 | #if 0 484 | { 485 | #endif 486 | cprintf("*** openat(%d, \"%s\", %x, %o)\n", dirfd, pathname, flags, mode); 487 | if (dirfd == AT_FDCWD) 488 | return open(pathname, flags, mode); 489 | return openat_next(dirfd, pathname, flags, mode); 490 | } 491 | 492 | _deffn ( int, openat64, (int dirfd, const char * pathname, int flags, mode_t mode) ) 493 | #if 0 494 | { 495 | #endif 496 | cprintf("*** openat64(%d, \"%s\", %x, %o)\n", dirfd, pathname, flags, mode); 497 | if (dirfd == AT_FDCWD) 498 | return open64(pathname, flags, mode); 499 | return openat64_next(dirfd, pathname, flags, mode); 500 | } 501 | 502 | _deffn ( FILE *, fopen, (const char * pathname, const char * mode) ) 503 | #if 0 504 | { 505 | #endif 506 | cprintf("*** fopen(\"%s\", \"%s\")\n", pathname, mode); 507 | char lnk[LNKMAX]; 508 | int o; 509 | if ((o = getlink(lnk, pathname)) == 0) 510 | return fopen_next(pathname, mode); 511 | 512 | if (strchr(mode, '+') != NULL) return NULL; 513 | 514 | int fd; 515 | if (mode[0] == 'r') 516 | fd = open_read(lnk, pathname + o); 517 | else 518 | fd = open_write(lnk, pathname + o, !!strchr(mode, 'a'), -1); 519 | 520 | if (fd < 0) return NULL; 521 | return fdopen(fd, mode); 522 | } 523 | 524 | #if 1 525 | _deffn ( int, stat, (const char * pathname, struct stat_x * statbuf) ) 526 | #if 0 527 | { 528 | #endif 529 | cprintf("*** stat(\"%s\", \"%p\")\n", pathname, statbuf); 530 | char lnk[LNKMAX]; 531 | int o; 532 | if ((o = getlink(lnk, pathname)) == 0) 533 | return stat_next(pathname, statbuf); 534 | 535 | return stat_it(lnk, pathname + o, statbuf); 536 | } 537 | _deffn ( int, lstat, (const char * pathname, struct stat_x * statbuf) ) 538 | #if 0 539 | { 540 | #endif 541 | cprintf("*** lstat(\"%s\", \"%p\")\n", pathname, statbuf); 542 | char lnk[LNKMAX]; 543 | int o; 544 | if ((o = getlink(lnk, pathname)) == 0) 545 | return lstat_next(pathname, statbuf); 546 | 547 | return stat_it(lnk, pathname + o, statbuf); 548 | } 549 | #endif 550 | 551 | _deffn ( int, __xstat, (int v, const char * pathname, struct stat_x * statbuf) ) 552 | #if 0 553 | { 554 | #endif 555 | cprintf("*** __xstat(%d, \"%s\", \"%p\")\n", v, pathname, statbuf); 556 | char lnk[LNKMAX]; 557 | int o; 558 | if ((o = getlink(lnk, pathname)) == 0) 559 | return __xstat_next(v, pathname, statbuf); 560 | 561 | return stat_it(lnk, pathname + o, statbuf); 562 | } 563 | 564 | _deffn ( int, __xstat64, (int v, const char * pathname, struct stat_x * statbuf) ) 565 | #if 0 566 | { 567 | #endif 568 | cprintf("*** __xstat64(%d, \"%s\", \"%p\")\n", v, pathname, statbuf); 569 | char lnk[LNKMAX]; 570 | int o; 571 | if ((o = getlink(lnk, pathname)) == 0) 572 | return __xstat64_next(v, pathname, statbuf); 573 | 574 | return stat_it(lnk, pathname + o, statbuf); 575 | } 576 | 577 | // fake lstat*s(), same code as in stat*s() 578 | _deffn ( int, __lxstat, (int v, const char * pathname, struct stat_x * statbuf) ) 579 | #if 0 580 | { 581 | #endif 582 | cprintf("*** __lxstat(%d, \"%s\", \"%p\")\n", v, pathname, statbuf); 583 | char lnk[LNKMAX]; 584 | int o; 585 | if ((o = getlink(lnk, pathname)) == 0) 586 | return __lxstat_next(v, pathname, statbuf); 587 | 588 | return stat_it(lnk, pathname + o, statbuf); 589 | } 590 | 591 | _deffn ( int, __lxstat64, (int v, const char * pathname, struct stat_x * statbuf) ) 592 | #if 0 593 | { 594 | #endif 595 | cprintf("*** __lxstat64(%d, \"%s\", \"%p\")\n", v, pathname, statbuf); 596 | char lnk[LNKMAX]; 597 | int o; 598 | if ((o = getlink(lnk, pathname)) == 0) 599 | return __lxstat64_next(v, pathname, statbuf); 600 | 601 | return stat_it(lnk, pathname + o, statbuf); 602 | } 603 | 604 | _deffn ( int, access, (const char * pathname, int mode) ) 605 | #if 0 606 | { 607 | #endif 608 | cprintf("*** access(\"%s\", %x)\n", pathname, mode); 609 | char lnk[LNKMAX]; 610 | int o; 611 | if ((o = getlink(lnk, pathname)) == 0) 612 | return access_next(pathname, mode); 613 | 614 | // dumb success... 615 | return 0; 616 | } 617 | -------------------------------------------------------------------------------- /src/lineread.ch: -------------------------------------------------------------------------------- 1 | 2 | /* ************************* lineread.c ************************* */ 3 | /* 4 | * lineread.c - functions to read lines from fd:s efficiently 5 | * 6 | * Created: Mon Jan 14 06:45:00 1991 too 7 | */ 8 | 9 | /* LICENSE: 2-clause BSD license ("Simplified BSD License"): */ 10 | 11 | struct lineread 12 | { 13 | char * currp; /* current scan point in buffer */ 14 | char * endp; /* pointer of last read character in buffer */ 15 | char * startp; /* pointer to start of output */ 16 | char * sizep; /* pointer to the end of read buffer */ 17 | int fd; /* input file descriptor */ 18 | char selected; /* has caller done select()/poll() or does he care */ 19 | char line_completed;/* line completion in LineRead */ 20 | char saved; /* saved char in LineRead */ 21 | char pad_unused; 22 | char data[LINEREAD_DATA_BUFFER_SIZE]; 23 | }; 24 | typedef struct lineread LineRead; 25 | 26 | static int lineread(LineRead * lr, char ** ptr) 27 | { 28 | int i; 29 | 30 | if (lr->currp == lr->endp) 31 | 32 | if (lr->selected) /* user called select() (or wants to block) */ 33 | { 34 | if (lr->line_completed) 35 | lr->startp = lr->currp = lr->data; 36 | 37 | // XXX add EINTR handling -- perhaps... // 38 | if ((i = read(lr->fd, 39 | lr->currp, 40 | lr->sizep - lr->currp)) <= 0) { 41 | /* 42 | * here if end-of-file or on error. set endp == currp 43 | * so if non-blocking I/O is in use next call will go to read() 44 | */ 45 | lr->endp = lr->currp; 46 | *ptr = (char *)(intptr_t)i; /* user compares ptr (NULL, (char *)-1, ... */ 47 | return -1; 48 | } 49 | else 50 | lr->endp = lr->currp + i; 51 | } 52 | else /* Inform user that next call may block (unless select()ed) */ 53 | { 54 | lr->selected = true; 55 | return 0; 56 | } 57 | else /* currp has not reached endp yet. */ 58 | { 59 | *lr->currp = lr->saved; 60 | lr->startp = lr->currp; 61 | } 62 | 63 | /* 64 | * Scan read string for next newline. 65 | */ 66 | while (lr->currp < lr->endp) 67 | if (*lr->currp++ == '\n') /* memchr ? (or rawmemchr & extra \n at end) */ 68 | { 69 | lr->line_completed = true; 70 | lr->saved = *lr->currp; 71 | *lr->currp = '\0'; 72 | lr->selected = false; 73 | *ptr = lr->startp; 74 | 75 | return lr->currp - lr->startp; 76 | } 77 | 78 | /* 79 | * Here if currp == endp, but no NLCHAR found. 80 | */ 81 | lr->selected = true; 82 | 83 | if (lr->currp == lr->sizep) { 84 | /* 85 | * Here if currp reaches end-of-buffer (endp is there also). 86 | */ 87 | if (lr->startp == lr->data) /* (data buffer too short for whole string) */ 88 | { 89 | lr->line_completed = true; 90 | *ptr = lr->data; 91 | *lr->currp = '\0'; 92 | return -1; 93 | } 94 | /* 95 | * Copy partial string to start-of-buffer and make control ready for 96 | * filling rest of buffer when next call to lineread() is made 97 | * (perhaps after select()). 98 | */ 99 | memmove(lr->data, lr->startp, lr->endp - lr->startp); 100 | lr->endp-= (lr->startp - lr->data); 101 | lr->currp = lr->endp; 102 | lr->startp = lr->data; 103 | } 104 | 105 | lr->line_completed = false; 106 | return 0; 107 | } 108 | 109 | static void lineread_init(LineRead * lr, int fd) 110 | { 111 | lr->fd = fd; 112 | lr->currp = lr->endp = (void*)0; /* any value works */ 113 | lr->sizep = lr->data + sizeof lr->data; 114 | lr->selected = lr->line_completed = true; 115 | } 116 | 117 | /* ^^^^^^^^^^^^^^^^^^^^^^^^^ lineread.c ^^^^^^^^^^^^^^^^^^^^^^^^^ */ 118 | -------------------------------------------------------------------------------- /src/lpktread.ch: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/domo141/ioiomxtx/b08e980f7c74b6a978efb4e48fcc9bded5c8eb93/src/lpktread.ch -------------------------------------------------------------------------------- /src/more-warnings.h: -------------------------------------------------------------------------------- 1 | #ifndef MORE_WARNINGS_H 2 | #define MORE_WARNINGS_H 1 3 | 4 | // (Ø) public domain, like https://creativecommons.org/publicdomain/zero/1.0/ 5 | 6 | #if defined(__linux__) && __linux__ || defined(__CYGWIN__) && __CYGWIN__ 7 | // on linux: man feature_test_macros -- try ftm.c at the end of it 8 | #define _DEFAULT_SOURCE 1 9 | // for older glibc's on linux (< 2.19 -- e.g. rhel7 uses 2.17...) 10 | #define _BSD_SOURCE 1 11 | #define _SVID_SOURCE 1 12 | #ifndef _POSIX_C_SOURCE 13 | #define _POSIX_C_SOURCE 200809L 14 | #endif 15 | #define _ATFILE_SOURCE 1 16 | // more extensions (less portability?) 17 | //#define _GNU_SOURCE 1 18 | #endif 19 | 20 | // hint: gcc -dM -E -xc /dev/null | grep -i gnuc 21 | // also: clang -dM -E -xc /dev/null | grep -i gnuc 22 | #if defined (__GNUC__) && defined (__STDC__) 23 | 24 | //#define WERROR 1 // uncomment or enter -DWERROR on command line/the includer 25 | 26 | #define DO_PRAGMA(x) _Pragma(#x) 27 | #if defined (WERROR) && WERROR 28 | #define PRAGMA_GCC_DIAG(w) DO_PRAGMA(GCC diagnostic error #w) 29 | #else 30 | #define PRAGMA_GCC_DIAG(w) DO_PRAGMA(GCC diagnostic warning #w) 31 | #endif 32 | 33 | #define PRAGMA_GCC_DIAG_E(w) DO_PRAGMA(GCC diagnostic error #w) 34 | #define PRAGMA_GCC_DIAG_W(w) DO_PRAGMA(GCC diagnostic warning #w) 35 | #define PRAGMA_GCC_DIAG_I(w) DO_PRAGMA(GCC diagnostic ignored #w) 36 | 37 | #if 0 // use of -Wpadded gets complicated, 32 vs 64 bit systems 38 | PRAGMA_GCC_DIAG_W (-Wpadded) 39 | #endif 40 | 41 | // to relax, change 'error' to 'warning' -- or even 'ignored' 42 | // selectively. use #pragma GCC diagnostic push/pop to change the 43 | // rules for a block of code in the source files including this. 44 | 45 | PRAGMA_GCC_DIAG (-Wall) 46 | PRAGMA_GCC_DIAG (-Wextra) 47 | 48 | #if __GNUC__ >= 8 // impractically strict in gccs 5, 6 and 7 49 | PRAGMA_GCC_DIAG (-Wpedantic) 50 | #endif 51 | 52 | #if __GNUC__ >= 7 || defined (__clang__) && __clang_major__ >= 12 53 | 54 | // gcc manual says all kind of /* fall.*through */ regexp's work too 55 | // but perhaps only when cpp does not filter comments out. thus... 56 | #define FALL_THROUGH __attribute__ ((fallthrough)) 57 | #else 58 | #define FALL_THROUGH ((void)0) 59 | #endif 60 | 61 | #ifndef __cplusplus 62 | PRAGMA_GCC_DIAG (-Wstrict-prototypes) 63 | PRAGMA_GCC_DIAG (-Wbad-function-cast) 64 | PRAGMA_GCC_DIAG (-Wold-style-definition) 65 | PRAGMA_GCC_DIAG (-Wmissing-prototypes) 66 | PRAGMA_GCC_DIAG (-Wnested-externs) 67 | #endif 68 | 69 | // -Wformat=2 ¡currently! (2020-11-11) equivalent of the following 4 70 | PRAGMA_GCC_DIAG (-Wformat) 71 | PRAGMA_GCC_DIAG (-Wformat-nonliteral) 72 | PRAGMA_GCC_DIAG (-Wformat-security) 73 | PRAGMA_GCC_DIAG (-Wformat-y2k) 74 | 75 | PRAGMA_GCC_DIAG (-Winit-self) 76 | PRAGMA_GCC_DIAG (-Wcast-align) 77 | PRAGMA_GCC_DIAG (-Wpointer-arith) 78 | PRAGMA_GCC_DIAG (-Wwrite-strings) 79 | PRAGMA_GCC_DIAG (-Wcast-qual) 80 | PRAGMA_GCC_DIAG (-Wshadow) 81 | PRAGMA_GCC_DIAG (-Wmissing-include-dirs) 82 | PRAGMA_GCC_DIAG (-Wundef) 83 | 84 | #ifndef __clang__ // XXX revisit -- tried with clang 3.8.0 85 | PRAGMA_GCC_DIAG (-Wlogical-op) 86 | #endif 87 | 88 | #ifndef __cplusplus // supported by c++ compiler but perhaps not worth having 89 | PRAGMA_GCC_DIAG (-Waggregate-return) 90 | #endif 91 | 92 | PRAGMA_GCC_DIAG (-Wmissing-declarations) 93 | PRAGMA_GCC_DIAG (-Wredundant-decls) 94 | PRAGMA_GCC_DIAG (-Winline) 95 | PRAGMA_GCC_DIAG (-Wvla) 96 | PRAGMA_GCC_DIAG (-Woverlength-strings) 97 | PRAGMA_GCC_DIAG (-Wuninitialized) 98 | 99 | //PRAGMA_GCC_DIAG (-Wfloat-equal) 100 | //PRAGMA_GCC_DIAG (-Wconversion) 101 | 102 | // avoiding known problems (turning some errors set above to warnings)... 103 | #if __GNUC__ == 4 104 | #ifndef __clang__ 105 | PRAGMA_GCC_DIAG_W (-Winline) // gcc 4.4.6 ... 106 | PRAGMA_GCC_DIAG_W (-Wuninitialized) // gcc 4.4.6, 4.8.5 ... 107 | #endif 108 | #endif 109 | 110 | #undef PRAGMA_GCC_DIAG_I 111 | #undef PRAGMA_GCC_DIAG_W 112 | #undef PRAGMA_GCC_DIAG_E 113 | #undef PRAGMA_GCC_DIAG 114 | #undef DO_PRAGMA 115 | 116 | #endif /* defined (__GNUC__) && defined (__STDC__) */ 117 | 118 | /* name and interface from talloc.c */ 119 | #ifndef discard_const_p // probably never defined, but... 120 | //#include 121 | #if defined (__INTPTR_TYPE__) /* e.g. gcc 4.8.5 - gcc 10.2 (2020-11-11) */ 122 | #define discard_const_p(type, ptr) ((type *)((__INTPTR_TYPE__)(ptr))) 123 | #elif defined (__PTRDIFF_TYPE__) /* e.g. gcc 4.4.6 */ 124 | #define discard_const_p(type, ptr) ((type *)((__PTRDIFF_TYPE__)(ptr))) 125 | #else 126 | #define discard_const_p(type, ptr) ((type *)(ptr)) 127 | #endif 128 | #endif 129 | 130 | #endif /* MORE_WARNINGS_H */ 131 | -------------------------------------------------------------------------------- /src/mxtx-dgramtunneld.c: -------------------------------------------------------------------------------- 1 | #if 0 /* -*- mode: c; c-file-style: "stroustrup"; tab-width: 8; -*- 2 | set -euf; trg=${0##*''/}; trg=${trg%.c}; test ! -e "$trg" || rm "$trg" 3 | case ${1-} in '') set x -O2; shift; esac 4 | #case ${1-} in '') set x -ggdb; shift; esac 5 | set -x; exec ${CC:-gcc} -std=c99 "$@" -o "$trg" "$0" -L. -lmxtx # -flto 6 | exit $? 7 | */ 8 | #endif 9 | /* 10 | * $ mxtx-dgramtunnel.c $ 11 | * 12 | * Author: Tomi Ollila -- too ät iki piste fi 13 | * 14 | * Copyright (c) 2018 Tomi Ollila 15 | * All rights reserved 16 | * 17 | * Created: Mon 05 Feb 2018 04:49:13 -0800 too 18 | * Last modified: Sat 02 Apr 2022 00:22:18 +0300 too 19 | */ 20 | 21 | // hint: strace -f [-o of] ... and strace -p ... are useful when inspecting... 22 | 23 | #include "more-warnings.h" 24 | 25 | #include 26 | #include 27 | #include 28 | #include 29 | #include 30 | #include 31 | #include 32 | #include 33 | #include 34 | #include 35 | #include 36 | #include 37 | #include 38 | #include 39 | #include 40 | #include 41 | 42 | #if defined(__linux__) && __linux__ || defined(__CYGWIN__) && __CYGWIN__ 43 | #include //for older compilers not defining __BYTE_ORDER__ & friends 44 | #else 45 | #include //ditto 46 | #endif 47 | 48 | #include "mxtx-lib.h" 49 | 50 | #define null ((void*)0) 51 | 52 | #define isizeof (int)sizeof 53 | 54 | #ifndef __BYTE_ORDER 55 | #define __BYTE_ORDER _BYTE_ORDER // opportunistic, picked from freebsd 56 | #define __LITTLE_ENDIAN _LITTLE_ENDIAN 57 | #define __BIG_ENDIAN _BIG_ENDIAN 58 | #endif 59 | 60 | // gcc -dM -E -x c /dev/null | grep BYTE 61 | 62 | //#if __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__ 63 | #if __BYTE_ORDER == __BIG_ENDIAN // from endian.h 64 | 65 | #define IADDR(a,b,c,d) ((in_addr_t)((a << 24) + (b << 16) + (c << 8) + d)) 66 | #define IPORT(v) ((in_port_t)(v)) 67 | #define A256(a) ((a)<<8) 68 | 69 | //#elif __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__ 70 | #elif __BYTE_ORDER == __LITTLE_ENDIAN // from endian.h 71 | 72 | #define IADDR(a,b,c,d) ((in_addr_t)(a + (b << 8) + (c << 16) + (d << 24))) 73 | #define IPORT(v) ((in_port_t)(((v) >> 8) | ((v) << 8))) 74 | #define A256(a) (a) 75 | 76 | #else 77 | #error unknown ENDIAN 78 | #endif 79 | 80 | #define DBG 0 81 | #if DBG 82 | #define dprintf(...) fprintf(stderr, __VA_ARGS__) 83 | #define dprintf0(...) do { } while (0) 84 | #else 85 | #define dprintf(...) do { } while (0) 86 | #define dprintf0(...) do { } while (0) 87 | #endif 88 | 89 | 90 | #define LPKTREAD_DATA_BUFFER_SIZE 4097 // seen 1272-sized udp datagrams... 91 | #define LPKTREAD_HAVE_PEEK 1 92 | #include "lpktread.ch" 93 | 94 | const char * _prg_ident = "mxtx-dgramtunneld: "; 95 | 96 | static void client(const char * lnk) ATTRIBUTE((noreturn)); 97 | static void server(void) ATTRIBUTE((noreturn)); 98 | 99 | int main(int argc, char * argv[]) 100 | { 101 | switch (argc) { 102 | case 1: server(); 103 | case 2: client(argv[1]); 104 | default: 105 | die("Usage: %s [link]", argv[0]); 106 | } 107 | } 108 | 109 | // we could use port zero (0) in bind, to let system choose 110 | // port, but this gives some more control and flexibility 111 | static int bind_dgram_isock_to_fd_3(int sp, int ep) 112 | { 113 | int sd = xsocket(AF_INET, SOCK_DGRAM); 114 | // don't for udp sockets... 115 | //int one = 1; 116 | //setsockopt(sd, SOL_SOCKET, SO_REUSEADDR, &one, sizeof one); 117 | struct sockaddr_in iaddr = { 118 | .sin_family = AF_INET, 119 | .sin_addr.s_addr = IADDR(127,0,0,1) 120 | }; 121 | for (int i = sp; i < ep; i++) { 122 | iaddr.sin_port = IPORT(i); 123 | if (bind(sd, (struct sockaddr *)&iaddr, sizeof iaddr) == 0) { 124 | xmovefd(sd, 3); 125 | return i; 126 | } 127 | if (errno == EADDRINUSE) 128 | continue; 129 | die("bind:"); 130 | } 131 | die("Cannot find free port"); 132 | } 133 | 134 | 135 | static bool initial_protosync(int fd, const char * sm, const char * em) 136 | { 137 | (void)!write(fd, sm, 8); 138 | char buf[8]; 139 | if (read(fd, buf, 8) != 8) { 140 | warn("No response from remote"); 141 | return false; 142 | } 143 | if (memcmp(buf, em, 8) != 0) { 144 | warn("Ident mismatch, expected '%s'", em); 145 | return false; 146 | } 147 | return true; 148 | } 149 | 150 | static void bind_dgram_usock_to_fd_4(const char * lnk) 151 | { 152 | struct sockaddr_un uaddr; 153 | int sd = xbind_listen_unix_socket( 154 | fill_mxtx_socket_path(&uaddr, lnk, "-dgt"), SOCK_DGRAM); 155 | xmovefd(sd, 4); 156 | } 157 | 158 | static void send_to_iport(uint32_t addr, in_port_t port, 159 | const unsigned char * data, int datalen) 160 | { 161 | struct sockaddr_in iaddr = { 162 | .sin_family = AF_INET, 163 | .sin_port = port, 164 | .sin_addr.s_addr = addr 165 | }; 166 | /* XXX */ sendto(3, data, datalen, 0, 167 | (struct sockaddr *)&iaddr, sizeof iaddr); 168 | } 169 | 170 | static bool connect_to_server_fd_5(const char * lnk) 171 | { 172 | int sd = connect_unix_stream_mxtx_socket(lnk, ""); 173 | if (sd < 0) return false; 174 | xmovefd(sd, 5); 175 | 176 | //char cmd[] = "....strace\0./mxtx-dgramtunneld"; 177 | char cmd[] = "....mxtx-dgramtunneld"; 178 | cmd[0] = cmd[1] = cmd[2] = 0; cmd[3] = sizeof cmd - 4; 179 | (void)!write(5, cmd, sizeof cmd); 180 | 181 | // in case of failure no need to close fd 5, will be dup2()d over next time 182 | return initial_protosync(5, "mxtxdtc1", "mxtxdts1"); 183 | } 184 | 185 | static void client(const char * lnk) 186 | { 187 | bind_dgram_usock_to_fd_4(lnk); // checkaa vielä portit XXX 188 | 189 | if (! connect_to_server_fd_5(lnk)) 190 | exit(1); 191 | 192 | int iport = bind_dgram_isock_to_fd_3(40001, 40404); 193 | 194 | // strace -f can be used to not let fork() detach when debugging... 195 | switch (fork()) { 196 | case -1: die("fork failed:"); 197 | case 0: break; 198 | default: exit(0); 199 | } 200 | /* child */ 201 | close(0); close(1); close(2); setsid(); // fd 2 to log (or caller does)? 202 | 203 | struct pollfd pfds[3] = { 204 | { .fd = 5, .events = POLLIN }, 205 | { .fd = 3, .events = POLLIN }, 206 | { .fd = 4, .events = POLLIN } 207 | }; 208 | struct { unsigned short sport; unsigned short cport; } portmap[1024]; 209 | memset(portmap, 0, sizeof portmap); 210 | time_t next_live_try = 0; 211 | time_t next_ping_try = time(null); 212 | int sent_after_recv = 0; 213 | LPktRead pr; 214 | lpktread_init(&pr, 5); 215 | 216 | while (1) { 217 | (void)poll(pfds, 3, -1); 218 | if (pfds[0].revents) { // msg from tunnel 219 | unsigned char * datap; 220 | int len; 221 | sent_after_recv = 0; 222 | while ( (len = lpktread(&pr, &datap)) != 0) { 223 | if (len < 3) { // XXX 224 | if (len == 2) goto next1; // pong received 225 | pfds[0].fd = -1; 226 | close(5); 227 | next_live_try = time(null); 228 | goto next1; 229 | } 230 | int sport = datap[0] * 256 + datap[1]; 231 | int cport = 0; 232 | for (size_t i = 0; i < sizeof portmap/sizeof portmap[0]; i++) { 233 | if (portmap[i].sport == sport) { 234 | cport = portmap[i].cport; 235 | break; 236 | } 237 | if (portmap[i].sport == 0) 238 | break; 239 | } 240 | if (cport == 0) { 241 | warn("cannot find port mapping for port %d", sport); 242 | goto next1; 243 | } 244 | int hip = ((sport - 1022) / 254) + 1; 245 | int lop = ((sport - 1022) % 254) + 1; 246 | send_to_iport(IADDR(127,0,hip,lop), IPORT(cport), 247 | datap + 2, len - 2); 248 | } 249 | } 250 | unsigned char sbuf[2048]; 251 | next1: 252 | if (pfds[1].revents) { // msg from mosh kilent 253 | struct sockaddr_in iaddr; 254 | socklen_t addrlen = sizeof iaddr; 255 | int len = recvfrom(3, sbuf + 4, sizeof sbuf - 4, 0, 256 | (struct sockaddr *)&iaddr, &addrlen); 257 | if (len < 0 || len > isizeof sbuf - 4) { 258 | die("XXX: len %d;", len); 259 | } 260 | if (next_live_try != 0) { 261 | // disconnected... 262 | time_t ct = time(null); 263 | if (ct - next_live_try < 0) goto next2; 264 | if (! connect_to_server_fd_5(lnk)) { 265 | next_live_try = ct + 10; 266 | goto next2; 267 | } 268 | next_live_try = 0; 269 | sent_after_recv = 0; 270 | pfds[0].fd = 5; 271 | } 272 | // else here and any optimization in next block unnecessary 273 | if (sent_after_recv > 20) { 274 | time_t ct = time(null); 275 | if (ct - next_ping_try > 0) { 276 | next_ping_try = ct + 10; 277 | int snt = send(5, "\000\002\000", 4, MSG_DONTWAIT); // ping 278 | if (snt != 4 || ++sent_after_recv > 20 + 60) { 279 | pfds[0].fd = -1; 280 | close(5); 281 | next_live_try = time(null); 282 | } 283 | } 284 | goto next2; 285 | } 286 | unsigned char i1 = ((unsigned char *)&iaddr.sin_addr.s_addr)[0]; 287 | unsigned char i2 = ((unsigned char *)&iaddr.sin_addr.s_addr)[1]; 288 | unsigned char i3 = ((unsigned char *)&iaddr.sin_addr.s_addr)[2]; 289 | unsigned char i4 = ((unsigned char *)&iaddr.sin_addr.s_addr)[3]; 290 | /**/ if (i1 != 127) 291 | warn("ip address octet 1 (%d) != 127", i1); 292 | else if (i2 != 0) 293 | warn("ip address octet 2 (%d) != 0", i2); 294 | else if (i3 == 0 || i3 == 255) 295 | warn("ip address octet 3 (%d) not in [1,254]", i3); 296 | else if (i4 == 0 || i4 == 255) 297 | warn("ip address octet 4 (%d) not in [1,254]", i4); 298 | else { 299 | int sport = (i3 - 1) * 254 + (i4 - 1) + 1022; 300 | int cport = IPORT(iaddr.sin_port); // IPORT works both ways... 301 | for (size_t i = 0; i < sizeof portmap/sizeof portmap[0]; i++) { 302 | if (portmap[i].sport == sport) { 303 | if (cport != portmap[i].cport) 304 | portmap[i].cport = cport; 305 | break; 306 | } 307 | if (portmap[i].cport == cport) { 308 | if (sport != portmap[i].sport) 309 | portmap[i].sport = sport; 310 | break; 311 | } 312 | if (portmap[i].cport == 0) { 313 | portmap[i].cport = cport; 314 | portmap[i].sport = sport; 315 | break; 316 | } 317 | } 318 | // XXX portmap table could in rare cases be too small 319 | sbuf[0] = (len + 2) / 256; sbuf[1] = (len + 2) % 256; 320 | sbuf[2] = sport / 256; sbuf[3] = sport % 256; 321 | /* XXX CHK */ (void)!write(5, sbuf, len + 4); 322 | sent_after_recv++; 323 | } 324 | } 325 | next2: 326 | if (pfds[2].revents) { 327 | // service port number requested 328 | struct sockaddr_un uaddr; 329 | socklen_t addrlen = sizeof uaddr; 330 | int len = recvfrom(4, sbuf, 8, 0, 331 | (struct sockaddr*)&uaddr, &addrlen); 332 | if (len != 2) { 333 | // might liveloop if not exit (probably eintr to be checked) 334 | if (len < 0) die("unexpected read failure..."); 335 | sbuf[0] = sbuf[1] = sbuf[2] = sbuf[3] = 0; 336 | goto reply; 337 | } 338 | if (sbuf[0] != 0 || sbuf[1] != 0) { // unexpected "version" 339 | sbuf[2] = sbuf[3] = 0; 340 | goto reply; 341 | } 342 | sbuf[2] = iport / 256; sbuf[3] = iport % 256; 343 | reply: 344 | (void)sendto(4, sbuf, 4, 0, (struct sockaddr*)&uaddr, addrlen); 345 | } 346 | } 347 | exit(0); 348 | } 349 | 350 | static void server(void) 351 | { 352 | if (! initial_protosync(1, "mxtxdts1", "mxtxdtc1")) 353 | exit(1); 354 | 355 | (void)bind_dgram_isock_to_fd_3(40501, 40904); 356 | 357 | struct pollfd pfds[2] = { 358 | { .fd = 1, .events = POLLIN }, 359 | { .fd = 3, .events = POLLIN } 360 | }; 361 | LPktRead pr; 362 | lpktread_init(&pr, 1); 363 | while (1) { 364 | (void)poll(pfds, 2, -1); 365 | if (pfds[0].revents) { 366 | unsigned char * datap; 367 | int len; 368 | while ( (len = lpktread(&pr, &datap)) > 0) { 369 | if (len < 3) { 370 | if (len == 2) { // ping 371 | const unsigned char * pp = _lpktread_peek(&pr, 4); 372 | // collapse consecutive pings into one // 373 | if (!pp || memcmp(pp, "\000\002\000", 4) != 0) 374 | (void)!write(1, "\000\002\000", 4); // pong 375 | goto next1; 376 | } 377 | die("tunnel data len %d < 2!", len); 378 | } 379 | int port = (datap[0] << 8) + datap[1]; 380 | send_to_iport(IADDR(127,0,0,1), IPORT(port), 381 | datap + 2, len - 2); 382 | // XXX w/ connrefused, howto info kilent ??? 383 | // XXX to be seen, improbable, user can RET ~ . 384 | } 385 | if (len < 0) { 386 | if (datap != null) 387 | warn("read from mxtx failed:"); 388 | else 389 | warn("EOF from mxtx"); 390 | exit(7); 391 | } 392 | } 393 | next1: 394 | if (pfds[1].revents) { 395 | char buf[2048]; 396 | struct sockaddr_in iaddr; 397 | socklen_t addrlen = sizeof iaddr; 398 | int l = recvfrom(3, buf + 4, sizeof buf - 4, 0, 399 | (struct sockaddr *)&iaddr, &addrlen); 400 | // XXX check addrlen (here & everywhere ?) 401 | if (l <= 0) 402 | die("XXX l = %d", l); 403 | int port = IPORT(iaddr.sin_port); 404 | buf[2] = port >> 8; buf[3] = port & 0xff; 405 | l += 2; 406 | buf[0] = l >> 8; buf[1] = l & 0xff; 407 | l += 2; 408 | if (write(1, buf, l) != l) 409 | // improve when shown needed 410 | die("short or failed write:"); 411 | } 412 | } 413 | exit(0); 414 | } 415 | -------------------------------------------------------------------------------- /src/mxtx-io.c: -------------------------------------------------------------------------------- 1 | #if 0 /* -*- mode: c; c-file-style: "stroustrup"; tab-width: 8; -*- 2 | set -euf; trg=${0##*''/}; trg=${trg%.c}; test ! -e "$trg" || rm "$trg" 3 | WARN="-Wall -Wstrict-prototypes -Winit-self -Wformat=2" # -pedantic 4 | WARN="$WARN -Wcast-align -Wpointer-arith " # -Wfloat-equal #-Werror 5 | WARN="$WARN -Wextra -Wwrite-strings -Wcast-qual -Wshadow" # -Wconversion 6 | WARN="$WARN -Wmissing-include-dirs -Wundef -Wbad-function-cast -Wlogical-op" 7 | WARN="$WARN -Waggregate-return -Wold-style-definition" 8 | WARN="$WARN -Wmissing-prototypes -Wmissing-declarations -Wredundant-decls" 9 | WARN="$WARN -Wnested-externs -Winline -Wvla -Woverlength-strings -Wpadded" 10 | case ${1-} in '') set x -O2; shift; esac 11 | #case ${1-} in '') set x -ggdb; shift; esac 12 | set -x; exec ${CC:-gcc} -std=c99 $WARN "$@" -o "$trg" "$0" -L. -lmxtx #-flto 13 | exit $? 14 | */ 15 | #endif 16 | /* 17 | * $ mxtx-io.c $ 18 | * 19 | * Author: Tomi Ollila -- too ät iki piste fi 20 | * 21 | * Copyright (c) 2017 Tomi Ollila 22 | * All rights reserved 23 | * 24 | * Created: Wed 16 Aug 2017 20:24:06 EEST too 25 | * Last modified: Sat 02 Apr 2022 00:22:18 +0300 too 26 | */ 27 | 28 | #include 29 | #include 30 | #include 31 | #include 32 | #include 33 | #include 34 | #include 35 | #include 36 | #include 37 | #include 38 | #include 39 | 40 | #include "mxtx-lib.h" 41 | 42 | #define null ((void*)0) 43 | 44 | const char * _prg_ident = "mxtx-io: "; 45 | 46 | static void io(int ifd, int ofd) 47 | { 48 | char buf[8192]; 49 | int l = read(ifd, buf, sizeof buf); 50 | if (l <= 0) { 51 | // hmm, econnreset... 52 | if (l == 0 || errno == ECONNRESET) { 53 | //warn("EOF from %d. Exiting.", ifd); 54 | exit(0); 55 | } 56 | // do we need EINTR handling. possibly not, (as not blocking) // 57 | die("read from %d failed:", ifd); 58 | } 59 | int wl = write(ofd, buf, l); 60 | if (wl != l) { 61 | if (wl < 0) 62 | die("write to %d failed:", ofd); 63 | else 64 | die("short write to %d (%d < %d)", ofd, wl, l); 65 | } 66 | } 67 | 68 | static int rexec(char * lnk, char ** rcmdv) 69 | { 70 | unsigned char rcmdline[1024]; 71 | unsigned char * p = rcmdline + 4; 72 | char *rcmd = rcmdv[0]; 73 | for (; *rcmdv; ++rcmdv) { 74 | size_t len = strlen(*rcmdv); 75 | if (len >= sizeof rcmdline - (p - rcmdline) - 1) 76 | die("command line %s... too long", rcmd); 77 | strcpy((char *)p, *rcmdv); 78 | p += len + 1; 79 | } 80 | rcmdline[0] = 0; 81 | rcmdline[1] = 0; 82 | rcmdline[2] = (p - rcmdline - 4) / 256; 83 | rcmdline[3] = (p - rcmdline - 4) & 255; 84 | 85 | int sd = connect_unix_stream_mxtx_socket(lnk, ""); 86 | if (sd < 0) exit(1); 87 | (void)!write(sd, rcmdline, p - rcmdline); 88 | return sd; 89 | } 90 | 91 | // optignore: for cases this used as 'ssh' command... 92 | static void optignore(int * argcp, char *** argvp) { 93 | while (*argcp > 0) { 94 | // warn("%d %s", *argcp, (*argvp)[0]); 95 | if ((*argvp)[0][0] != '-') 96 | break; 97 | if (strcmp((*argvp)[0], "-e") == 0) { 98 | *argcp -= 2; *argvp += 2; 99 | } 100 | else 101 | break; 102 | } 103 | } 104 | 105 | static int is_link(char * arg) 106 | { 107 | if (arg[0] == ':' && arg[1] == '\0') return 0; 108 | char * p = strrchr(arg, ':'); 109 | if (p && p[1] == '\0') { 110 | p[0] = '\0'; 111 | return 1; 112 | } 113 | return 0; 114 | } 115 | 116 | int main(int argc, char ** argv) 117 | { 118 | char * prgname = argv[0]; 119 | char * sep = null; 120 | 121 | argc--; argv++; 122 | 123 | // check first arg being separator, e.g '' '.' '..' '*/' '*/.' '*/..' 124 | if (argc > 0) { 125 | char * p = strrchr(argv[0], '/'); 126 | if (p == null) p = argv[0]; else p++; 127 | if (p[0] == '\0') sep = argv[0]; 128 | else if (p[0] == '.') { 129 | if (p[1] == '\0' || (p[1] == '.' && p[2] == '\0')) 130 | sep = argv[1]; 131 | } 132 | } 133 | if (sep) { argc--; argv++; } 134 | 135 | optignore(&argc, &argv); 136 | 137 | if (argc < 1) { 138 | fprintf(stderr, "\nUsage: %s [sep] [link] command [args] " 139 | "[ [link:] command [args]]\n\n\ 140 | 'by default' %s runs command on linked system and communicates via stdio.\n\ 141 | \n\ 142 | when first arg matches '', '.', '..', '*/', '*/.', '*/..', it is considered\n\ 143 | as separator string for 2 command lines given on command line. in this case\n\ 144 | if next arg (in both command lines) end with ':', then that command is run\n\ 145 | on linked system (otherwise directly) and (std)io of both of these commands\n\ 146 | are tied together.\n\n", prgname, prgname); 147 | exit(1); 148 | } 149 | 150 | char ** rcmdl2 = null; 151 | 152 | if (sep) { 153 | for (int i = 0; argv[i]; i++) { 154 | if (strcmp(argv[i], sep) == 0) { 155 | if (argv[i + 1] == null) 156 | die("Second command line missing after '%s'" 157 | " on command line", sep); 158 | rcmdl2 = &argv[i + 1]; 159 | argv[i] = null; 160 | } 161 | } 162 | if (rcmdl2 == null) 163 | die("Second '%s' missing on command line", sep); 164 | } 165 | else if (argc < 2) 166 | die("Command missing"); 167 | 168 | /// XXX unify above and below (if you care) /// 169 | 170 | int sd1, sd2, sdo; 171 | if (!sep) { 172 | is_link(argv[0]); 173 | char * lnk = argv[0]; 174 | argc--; argv++; 175 | optignore(&argc, &argv); 176 | if (argc < 1) die("Command missing"); 177 | sd1 = rexec(lnk, argv); 178 | sd2 = 0; 179 | sdo = 1; 180 | // continue to io loop // 181 | } 182 | else { 183 | int r1 = is_link(argv[0]); 184 | int r2 = is_link(rcmdl2[0]); 185 | 186 | /**/ if (r1 && ! r2) { 187 | close(0); 188 | sd1 = rexec(argv[0], argv + 1); 189 | xmovefd(sd1, 0); // expect it to be 0, though 190 | xdup2(0, 1); 191 | xexecvp(rcmdl2[0], rcmdl2); 192 | // not reached // 193 | } 194 | else if (r2 && ! r1) { 195 | close(0); 196 | sd2 = rexec(rcmdl2[0], rcmdl2 + 1); 197 | xmovefd(sd2, 0); // expect it to be 0, though 198 | xdup2(0, 1); 199 | xexecvp(argv[0], argv); 200 | // not reached // 201 | } 202 | else if (! r1) { // both local processes (much like ioio.pl) 203 | int sv[2]; 204 | if (socketpair(AF_UNIX, SOCK_STREAM, 0, sv) < 0) 205 | die("socketpair"); 206 | 207 | pid_t pid = fork(); 208 | if (pid == 0) { 209 | // child // 210 | close(sv[0]); 211 | xdup2(sv[1], 0); 212 | xdup2(sv[1], 1); 213 | close(sv[1]); 214 | xexecvp(*argv, argv); 215 | // not reached // 216 | } 217 | if (pid < 0) die("fork:"); 218 | // parent // 219 | close(sv[1]); 220 | xdup2(sv[0], 0); 221 | xdup2(sv[0], 1); 222 | close(sv[0]); 223 | xexecvp(*rcmdl2, rcmdl2); 224 | // not reached // 225 | } 226 | else { 227 | sd1 = rexec(argv[0], argv + 1); 228 | sd2 = rexec(rcmdl2[0], rcmdl2 + 1); 229 | sdo = sd2; 230 | // continue to io loop // 231 | } 232 | } 233 | 234 | struct pollfd pfds[2]; 235 | pfds[0].fd = sd1; pfds[1].fd = sd2; 236 | pfds[0].events = pfds[1].events = POLLIN; 237 | 238 | while (1) { 239 | if (poll(pfds, 2, -1) <= 0) { 240 | warn("unexpected poll() return without any input available."); 241 | sleep(1); 242 | } 243 | if (pfds[0].revents) io(sd1, sdo); 244 | if (pfds[1].revents) io(sd2, sd1); 245 | } 246 | return 0; 247 | } 248 | -------------------------------------------------------------------------------- /src/mxtx-lib.c: -------------------------------------------------------------------------------- 1 | #if 0 /* -*- mode: c; c-file-style: "stroustrup"; tab-width: 8; -*- 2 | set -euf; trg=${0##*''/}; trg=${trg%.c}.o; test ! -e "$trg" || rm "$trg" 3 | WARN="-Wall -Wstrict-prototypes -Winit-self -Wformat=2" # -pedantic 4 | WARN="$WARN -Wcast-align -Wpointer-arith " # -Wfloat-equal #-Werror 5 | WARN="$WARN -Wextra -Wwrite-strings -Wcast-qual -Wshadow" # -Wconversion 6 | WARN="$WARN -Wmissing-include-dirs -Wundef -Wbad-function-cast -Wlogical-op" 7 | WARN="$WARN -Waggregate-return -Wold-style-definition" 8 | WARN="$WARN -Wmissing-prototypes -Wmissing-declarations -Wredundant-decls" 9 | WARN="$WARN -Wnested-externs -Winline -Wvla -Woverlength-strings -Wpadded" 10 | case ${1-} in '') set x -O2; shift; esac 11 | #case ${1-} in '') set x -ggdb; shift; esac 12 | set -x; exec ${CC:-gcc} -std=c99 $WARN "$@" -o "$trg" -c "$0" # -flto 13 | exit $? 14 | */ 15 | #endif 16 | /* 17 | * $ mxtx-lib.c $ 18 | * 19 | * Author: Tomi Ollila -- too ät iki piste fi 20 | * 21 | * Copyright (c) 2017 Tomi Ollila 22 | * All rights reserved 23 | * 24 | * Created: Wed 16 Aug 2017 21:06:56 EEST too 25 | * Last modified: Sun 11 Nov 2018 23:31:01 +0200 too 26 | */ 27 | 28 | /* sh mxtx-lib.c will compile single mxtx-lib.o -- good for testing 29 | * that new code works. perl -x mxtx-lib.c will compile libmxtx.a 30 | */ 31 | 32 | #define _GNU_SOURCE 1 // for struct ucred 33 | #define _POSIX_C_SOURCE 199309L // sigaction 34 | 35 | #include 36 | #include 37 | #include 38 | #include 39 | #include 40 | #include 41 | #include 42 | #include 43 | #include 44 | #include 45 | #include 46 | #include 47 | 48 | #include "mxtx-lib.h" 49 | 50 | extern char * _prg_ident; 51 | void vwarn(const char * format, va_list ap) 52 | { 53 | int error = errno; 54 | 55 | //fputs(timestr(), stderr); 56 | fputs(_prg_ident, stderr); 57 | vfprintf(stderr, format, ap); 58 | if (format[strlen(format) - 1] == ':') 59 | fprintf(stderr, " %s\n", strerror(error)); 60 | else 61 | fputs("\n", stderr); 62 | fflush(stderr); 63 | } 64 | void warn(const char * format, ...) 65 | { 66 | va_list ap; 67 | 68 | va_start(ap, format); 69 | vwarn(format, ap); 70 | va_end(ap); 71 | } 72 | void die(const char * format, ...) 73 | { 74 | va_list ap; 75 | 76 | va_start(ap, format); 77 | vwarn(format, ap); 78 | va_end(ap); 79 | exit(1); 80 | } 81 | 82 | void seterr_linebuf(void) 83 | { 84 | static char buf[1024]; 85 | setvbuf(stderr, buf, _IOLBF, sizeof buf); 86 | } 87 | 88 | void sigact(int sig, void (*handler)(int), int flags) 89 | { 90 | struct sigaction action = { .sa_handler = handler, 91 | /* NOCLDSTOP needed if ptraced */ 92 | .sa_flags = flags|SA_NOCLDSTOP }; 93 | sigemptyset(&action.sa_mask); 94 | sigaction(sig, &action, NULL); 95 | } 96 | 97 | int xsocket(int domain, int type) 98 | { 99 | int sd = socket(domain, type, 0); 100 | if (sd < 0) die("socket:"); 101 | return sd; 102 | } 103 | 104 | void xdup2(int o, int n) 105 | { 106 | if (dup2(o, n) < 0) 107 | die("dup2:"); 108 | } 109 | 110 | int xfcntl(int fd, int cmd, int arg) 111 | { 112 | int rv = fcntl(fd, cmd, arg); 113 | if (rv < 0) die("fcntl:"); 114 | return rv; 115 | } 116 | 117 | void xexecvp(const char * file, char *const argv[]) 118 | { 119 | execvp(file, argv); 120 | die("execvp: %s ...:", file); 121 | } 122 | 123 | void set_nonblock(int fd) 124 | { 125 | xfcntl(fd, F_SETFL, xfcntl(fd, F_GETFL, 0) | O_NONBLOCK); 126 | } 127 | 128 | void xreadfully(int fd, void * buf, ssize_t len) 129 | { 130 | if (len <= 0) return; 131 | 132 | while (1) { 133 | int l = read(fd, buf, len); 134 | 135 | if (l >= len) return; 136 | 137 | if (l <= 0) { 138 | if (l == 0) die("EOF from %d", fd); 139 | if (errno == EINTR) continue; 140 | else die("read(%d, ...) failed:", fd); 141 | } 142 | buf = (char *)buf + l; 143 | len -= l; 144 | } 145 | } 146 | 147 | void xwritefully(int fd, const void * buf, ssize_t len) 148 | { 149 | if (len <= 0) return; 150 | 151 | while (1) { 152 | int l = write(fd, buf, len); 153 | 154 | if (l >= len) return; 155 | 156 | if (l <= 0) { 157 | if (l == 0) { sleep(1); continue; } 158 | if (errno == EAGAIN) { sleep(1); continue; } 159 | if (errno == EINTR) continue; 160 | die("write(%d, ..., %zd) failed:", fd, len); 161 | } 162 | buf = (const char *)buf + l; 163 | len -= l; 164 | } 165 | } 166 | 167 | bool checkpeerid(int sd) 168 | { 169 | #if defined(__linux__) && __linux__ || defined(__CYGWIN__) && __CYGWIN__ 170 | struct ucred cr; 171 | socklen_t len = sizeof cr; 172 | if (getsockopt(sd, SOL_SOCKET, SO_PEERCRED, &cr, &len) < 0) { 173 | warn("getsockopt SO_PEERCRED on channel %d failed:", sd); 174 | return false; 175 | } 176 | int uid = (int)getuid(); 177 | if ((int)cr.uid != uid) { 178 | warn("Peer real uid %d not %d on channel %d", (int)cr.uid, uid, sd); 179 | return false; 180 | } 181 | return true; 182 | #elif __XXXsolaris__ // fixme, when known what and how... 183 | //getpeerucred(...); 184 | warn("Peer check unimplemented"); 185 | return false; 186 | #else 187 | // fallback default -- so far not tested anywhere // 188 | int peuid, pegid; 189 | if (getpeereid(sd, &peuid, &pegid) < 0) { 190 | warn("getpeereid on channel %d failed:", sd); 191 | return false; 192 | } 193 | int euid = (int)geteuid(); 194 | if (peuid != euid) { 195 | warn("Peer effective uid %d not %d on channel %d",(int)peuid,euid, sd); 196 | return false; 197 | } 198 | return true; 199 | #endif 200 | } 201 | 202 | // :sockaddr_un.o 203 | struct sockaddr_un * fill_sockaddr_un(struct sockaddr_un * addr, 204 | const char * format, ...) 205 | { 206 | va_list ap; 207 | memset(addr, 0, sizeof *addr); 208 | va_start(ap, format); 209 | // we lose one char. perhaps that is tolerable 210 | int pathlen = vsnprintf(addr->sun_path, sizeof addr->sun_path, format, ap); 211 | va_end(ap); 212 | addr->sun_family = pathlen; 213 | return addr; 214 | } 215 | 216 | // :fillpath.o 217 | struct sockaddr_un * fill_mxtx_socket_path(struct sockaddr_un * uaddr, 218 | const char * path, const char * x) 219 | { 220 | if (strchr(path, '/') != NULL) 221 | return fill_sockaddr_un(uaddr, "%s%s", x, path); 222 | 223 | // else 224 | #if defined(__linux__) && __linux__ 225 | uid_t uid = getuid(); 226 | return fill_sockaddr_un(uaddr, "%c/tmp/user-%d/mxtx%s,%s",0, uid, x, path); 227 | #else 228 | char * xdgrd = getenv("XDG_RUNTIME_DIR"); 229 | if (xdgrd) 230 | return fill_sockaddr_un(uaddr, "%s/mxtx%s,%s", xdgrd, x, path); 231 | // else 232 | uid_t uid = getuid(); 233 | char dir[64]; 234 | (void)snprintf(dir, sizeof dir, "/tmp/user-%d", uid); 235 | (void)mkdir(dir, 0700); 236 | return fill_sockaddr_un(uaddr, "/tmp/user-%d/mxtx%s,%s", uid, x, path); 237 | #endif 238 | } 239 | 240 | // :usconnect.o 241 | int connect_unix_stream_socket(struct sockaddr_un * addr) 242 | { 243 | int sd = socket(AF_UNIX, SOCK_STREAM, 0); 244 | if (sd < 0) { 245 | warn("creating socket failed:"); 246 | return -1; 247 | } 248 | int one = 1; 249 | setsockopt(sd, SOL_SOCKET, SO_KEEPALIVE, &one, sizeof one); 250 | 251 | unsigned int pathlen = addr->sun_family; 252 | addr->sun_family = AF_UNIX; 253 | 254 | if (pathlen >= sizeof addr->sun_path) { 255 | warn("socket path too long (%d octets)", pathlen); 256 | goto closerr; 257 | } 258 | pathlen += offsetof(struct sockaddr_un, sun_path); 259 | if (connect(sd, (struct sockaddr *)addr, pathlen) < 0) { 260 | if (addr->sun_path[0] == '\0') addr->sun_path[0] = '@'; 261 | warn("connecting %s failed:", addr->sun_path); 262 | goto closerr; 263 | } 264 | if (! checkpeerid(sd)) 265 | goto closerr; 266 | 267 | return sd; 268 | closerr: 269 | close(sd); 270 | return -1; 271 | } 272 | 273 | // XXX mxtx nimeen jotenki 274 | int connect_unix_stream_mxtx_socket(const char * path, const char * x) 275 | { 276 | struct sockaddr_un addr; 277 | return connect_unix_stream_socket(fill_mxtx_socket_path(&addr, path, x)); 278 | } 279 | 280 | 281 | int xbind_listen_unix_socket(struct sockaddr_un * addr, int type) 282 | { 283 | int sd = xsocket(AF_UNIX, type); 284 | int one = 1; 285 | #if 0 286 | close(sd); sd = socket(AF_INET, type, 0); 287 | struct sockaddr_in * iaddr = (struct sockaddr_in *)addr; 288 | memset(iaddr, 0, sizeof *iaddr); //INADDR_ANY 289 | iaddr->sin_family = AF_INET; 290 | iaddr->sin_port = htons(1080); 291 | #endif 292 | setsockopt(sd, SOL_SOCKET, SO_REUSEADDR, &one, sizeof one); 293 | 294 | unsigned int pathlen = addr->sun_family; 295 | addr->sun_family = AF_UNIX; 296 | 297 | if (pathlen >= sizeof addr->sun_path) 298 | die("socket path too long (%d octets)", pathlen); 299 | 300 | pathlen += offsetof(struct sockaddr_un, sun_path); 301 | if (bind(sd, (struct sockaddr *)addr, pathlen) < 0) { 302 | if (errno == EADDRINUSE) 303 | die("bind: address already in use\n" 304 | "The socket '%s' exists and may be live\n" 305 | "Remove the file and try again if the socket is stale", 306 | addr->sun_path[0]? addr->sun_path: addr->sun_path + 1); 307 | die("bind:"); 308 | } 309 | if (type == SOCK_STREAM && listen(sd, 5) < 0) 310 | die("listen:"); 311 | 312 | return sd; 313 | } 314 | 315 | 316 | #if 0 /* 317 | #!perl 318 | #line 318 319 | #---- 318 320 | 321 | use 5.8.1; 322 | use strict; 323 | use warnings; 324 | 325 | unlink 'mxtx-lib.a', 'mxtx-lib.wip'; 326 | system qw/rm -rf _tmp/ if -d '_tmp'; 327 | mkdir '_tmp'; 328 | open I, '<', 'src/mxtx-lib.c' or die; 329 | my @lead; 330 | while () { 331 | if ( /include.*mxtx-lib.h/) { 332 | push @lead, qq'#include "../src/mxtx-lib.h"\n'; 333 | last; 334 | } 335 | push @lead, $_; 336 | } 337 | 338 | chdir '_tmp'; 339 | 340 | my @llad; 341 | my $name = ''; 342 | while () { 343 | push @llad, $_; 344 | $name = $1, next if m|^// :(\S+)[.]o|; 345 | if (/^[a-z].*?\s(\S+)\(.*\)\s*(\/\/|$)/) { 346 | $name = $1 unless $name; 347 | open O, '>', $name . '.c'; 348 | print O @lead, "\n"; 349 | my $l = $. - @llad; 350 | print O "#line $l\n"; 351 | print O @llad, "\n"; 352 | @llad = (); 353 | while () { 354 | print O $_; 355 | last if /^}/; 356 | } 357 | close O; 358 | system qw/sh/, "$name.c"; 359 | die if $?; 360 | $name = ''; 361 | } 362 | } 363 | 364 | chdir '..'; 365 | system qw/ar qvs libmxtx.wip/, <_tmp/?*.o>; 366 | rename 'libmxtx.wip', 'libmxtx.a'; 367 | print "created 'libmxtx.a'\n"; 368 | system qw/rm -rf _tmp/; 369 | __END__ 370 | */ 371 | #endif 372 | -------------------------------------------------------------------------------- /src/mxtx-lib.h: -------------------------------------------------------------------------------- 1 | #if 0 /* -*- mode: c; c-file-style: "stroustrup"; tab-width: 8; -*- 2 | set -x; : test compile :; exec ${CC:-gcc} "$0" 3 | exit $? 4 | */ 5 | #endif 6 | /* 7 | * $ mxtx-lib.h $ 8 | * 9 | * Author: Tomi Ollila -- too ät iki piste fi 10 | * 11 | * Copyright (c) 2017 Tomi Ollila 12 | * All rights reserved 13 | * 14 | * Created: Wed 16 Aug 2017 21:07:44 EEST too 15 | * Last modified: Sat 24 Feb 2018 16:48:59 +0200 too 16 | */ 17 | 18 | #ifndef MXTX_LIB_H 19 | #define MXTX_LIB_H 20 | 21 | // add just the header files to make cc mxtx-lib.h to compile 22 | 23 | #ifndef NO_DIEWARN_PROTOS 24 | #include 25 | #endif 26 | #include 27 | 28 | // clang -dM -E - = 4) 30 | #define ATTRIBUTE(x) __attribute__(x) 31 | #else 32 | #define ATTRIBUTE(x) 33 | #endif 34 | 35 | // w/ this define defining _DEFAULT_SOURCE or _BSD_SOURCE can be avoided... 36 | // but, seterr_linebuf... 37 | //#define setlinebuf(stream) setvbuf((stream), NULL, _IOLBF, 0) 38 | 39 | #ifndef NO_DIEWARN_PROTOS 40 | void vwarn(const char * format, va_list ap); 41 | void warn(const char * format, ...) ATTRIBUTE ((format (printf, 1, 2))); 42 | void die(const char * format, ...) 43 | ATTRIBUTE ((format (printf, 1, 2))) ATTRIBUTE ((noreturn)); 44 | #endif 45 | 46 | void seterr_linebuf(void); 47 | 48 | // be specific on system where to check/set SA_NOCLDWAIT 49 | #if defined (__CYGWIN__) && __CYGWIN__ 50 | //#ifndef SA_NOCLDWAIT 51 | #define SA_NOCLDWAIT 0 // seems to be #defined to 2 on linux // 52 | //#endif 53 | #endif 54 | 55 | void sigact(int sig, void (*handler)(int), int flags); 56 | 57 | int xsocket(int domain, int type); 58 | void xdup2(int o, int n); 59 | int xfcntl(int fd, int cmd, int arg); 60 | void xexecvp(const char * file, char *const argv[]) ATTRIBUTE ((noreturn)); 61 | void set_nonblock(int fd); 62 | 63 | void xreadfully(int fd, void * buf, ssize_t len); 64 | void xwritefully(int fd, const void * buf, ssize_t len); 65 | 66 | struct sockaddr_un; 67 | struct sockaddr_un * fill_sockaddr_un(struct sockaddr_un * addr, 68 | const char * format, ...) 69 | ATTRIBUTE ((format (printf, 2, 3))); 70 | struct sockaddr_un * fill_mxtx_socket_path(struct sockaddr_un * uaddr, 71 | const char * path, const char * x); 72 | bool checkpeerid(int sd); 73 | 74 | int connect_unix_stream_socket(struct sockaddr_un * addr); 75 | int connect_unix_stream_mxtx_socket(const char * path, const char * x); 76 | int xbind_listen_unix_socket(struct sockaddr_un * addr, int type); 77 | 78 | static inline void xmovefd(int ofd, int nfd) { 79 | if (ofd == nfd) return; 80 | xdup2(ofd, nfd); close(ofd); 81 | } 82 | 83 | #endif /* MXTX_LIB_H */ 84 | -------------------------------------------------------------------------------- /src/mxtx-rsh.c: -------------------------------------------------------------------------------- 1 | #if 0 /* -*- mode: c; c-file-style: "stroustrup"; tab-width: 8; -*- 2 | set -euf; trg=${0##*''/}; trg=${trg%.c}; test ! -e "$trg" || rm "$trg" 3 | case ${1-} in '') set x -O2; shift; esac 4 | #case ${1-} in '') set x -ggdb; shift; esac 5 | set -x; exec ${CC:-gcc} -std=c99 "$@" -o "$trg" "$0" -L. -lmxtx 6 | exit $? 7 | */ 8 | #endif 9 | /* 10 | * $ mxtx-rsh.c $ 11 | * 12 | * Author: Tomi Ollila -- too ät iki piste fi 13 | * 14 | * Copyright (c) 2017 Tomi Ollila 15 | * All rights reserved 16 | * 17 | * Created: Sun 03 Sep 2017 21:45:01 EEST too 18 | * Last modified: Sat 02 Apr 2022 00:22:18 +0300 too 19 | */ 20 | 21 | #include "more-warnings.h" 22 | 23 | // for linux to compile w/ -std=c99 24 | //#define _DEFAULT_SOURCE // Since glibc 2.19: -- defined in more-warnings.h 25 | //#define _BSD_SOURCE // Glibc 2.19 and earlier: -- ditto 26 | 27 | #include 28 | #include 29 | //#include 30 | #include 31 | //#include 32 | //#include 33 | #include 34 | #include 35 | #include 36 | #include 37 | #include 38 | #if defined(__linux__) && __linux__ || defined(__CYGWIN__) && __CYGWIN__ 39 | #include 40 | #else 41 | #include 42 | #endif 43 | //#include <.h> 44 | 45 | #include "mxtx-lib.h" 46 | 47 | #include "mxtx-rsh.ch" 48 | #define LPKTREAD_DATA_BUFFER_SIZE (65532 - 3) 49 | #include "lpktread.ch" 50 | 51 | #define null ((void*)0) 52 | 53 | #define BB { 54 | #define BE } 55 | 56 | #define isizeof (int)sizeof 57 | 58 | #define df(...) fprintf(stderr, __VA_ARGS__) 59 | 60 | const char * _prg_ident = "mxtx-rsh: "; 61 | 62 | struct { 63 | char waitsigack; 64 | char mayhavesig; 65 | char sighup; 66 | char sigint; 67 | char sigquit; 68 | char sigterm; 69 | char tty; 70 | char nostdin; 71 | char * link; 72 | char env_link[128]; 73 | struct termios saved_tio; 74 | #if defined (__LP64__) && __LP64__ 75 | int pad2; 76 | #endif 77 | } G; 78 | 79 | static void reset_tio(void) 80 | { 81 | tcsetattr(0, TCSAFLUSH, &G.saved_tio); 82 | } 83 | 84 | static void sighandler(int sig); 85 | 86 | static void hook_exit(void); 87 | static void may_run_hook(bool entry) 88 | { 89 | char buf[4096]; 90 | const char * home = getenv("HOME"); 91 | if (home == null) return; // unlikely ! 92 | size_t l = (size_t)snprintf(buf, sizeof buf, 93 | "%s/.local/share/mxtx/rsh-hook", home); 94 | if (l >= sizeof buf) return; // unlikely ! 95 | if (access(buf, F_OK | X_OK) != 0) { 96 | if (errno != ENOENT) warn("%s:", buf); 97 | return; // if dir: user error // 98 | } 99 | pid_t pid = fork(); 100 | if (pid < 0) die("fork():"); 101 | if (pid > 0) { 102 | if (entry) atexit(hook_exit); 103 | waitpid(pid, NULL, 0); 104 | return; 105 | } 106 | // child // 107 | if (entry) setenv("MXTX_RSH_ENTRY", "t", 1); 108 | else setenv("MXTX_RSH_ENTRY", "", 1); 109 | char * args[3]; 110 | args[0] = buf; 111 | args[1] = G.link; 112 | args[2] = null; 113 | xexecvp(buf, args); 114 | } 115 | static void hook_exit(void) { 116 | may_run_hook(false); 117 | } 118 | 119 | static void opts(int * argcp, char *** argvp) { 120 | while (*argcp > 0 && (*argvp)[0][0] == '-') { 121 | /**/ if ((*argvp)[0][1] == 't' && (*argvp)[0][2] == '\0') 122 | G.tty = 1; 123 | else if ((*argvp)[0][1] == 'n' && (*argvp)[0][2] == '\0') 124 | G.nostdin = 1; 125 | else if ((*argvp)[0][1] == 'q' && (*argvp)[0][2] == '\0') 126 | ; // drop -q (quiet)... 127 | else if ((*argvp)[0][1] == 'e' && (*argvp)[0][2] == '\0') { 128 | // drop -e (for now) 129 | (*argcp)--; (*argvp)++; 130 | } 131 | else 132 | die("'%s': unknown option", (*argvp)[0]); 133 | (*argcp)--; (*argvp)++; 134 | continue; 135 | } 136 | } 137 | 138 | 139 | int main(int argc, char * argv[]) 140 | { 141 | int caa = 0; 142 | const char * prgname = argv[0]; 143 | argc--; argv++; 144 | opts(&argc, &argv); 145 | 146 | #define USAGEFMT "Usage: %s [-t] [-n] remote [.] [command] [args]" 147 | if (argc <= 0) die(USAGEFMT, prgname); 148 | 149 | G.link = argv[0]; 150 | argc--; argv++; 151 | opts(&argc, &argv); 152 | 153 | if (argc < 0) die(USAGEFMT, prgname); 154 | #undef USAGEFMT 155 | 156 | if (argc == 0 && G.nostdin == 0) 157 | G.tty = 1; 158 | else if (argc > 0 && argv[0][0] == '.' && argv[0][1] == '\0') { 159 | if (argc == 1) 160 | die("After '.' command is required"); 161 | caa = 1; 162 | } 163 | 164 | if (G.tty && tcgetattr(0, &G.saved_tio) < 0) { 165 | warn("Cannot read tty parameters. force remote tty mode"); 166 | G.tty = -1; // tristate // 167 | } 168 | 169 | if (argc == 0) 170 | may_run_hook(true); 171 | BB; 172 | int sd = connect_unix_stream_mxtx_socket(G.link, ""); 173 | if (sd < 0) exit(1); 174 | xmovefd(sd, 3); 175 | BE; 176 | (void)!write(3, "\0\0\0\012" "mxtx-rshd\0" MXTX_RSHC_IDENT, 177 | 4 + 10 + sizeof mxtx_rshc_ident); 178 | LPktRead pr; 179 | BB; 180 | unsigned char * p = (unsigned char *)pr.data; 181 | if (G.tty) { 182 | struct winsize ws; 183 | if (G.tty < 0) { 184 | ws.ws_col = 80, ws.ws_row = 24; 185 | } 186 | else if (ioctl(0, TIOCGWINSZ, &ws) < 0) { 187 | warn("Getting window size failed (using 80x24):"); 188 | ws.ws_col = 80, ws.ws_row = 24; 189 | } 190 | *p++ = '\0'; *p++ = '\005'; *p++ = 't'; 191 | *p++ = ws.ws_col / 256; *p++ = ws.ws_col % 256; 192 | *p++ = ws.ws_row / 256; *p++ = ws.ws_row % 256; 193 | } 194 | snprintf(G.env_link, sizeof G.env_link, "MXTX_LINK=%s", G.link); 195 | putenv(G.env_link); 196 | const char * pe[] = { "TERM","LANG","LANGUAGE","LC_CTYPE","LC_NUMERIC", 197 | "LC_TIME","LC_COLLATE","LC_MONETARY","LC_MESSAGES", 198 | "LC_PAPER","LC_NAME","LC_ADDRESS","LC_TELEPHONE", 199 | "LC_MEASUREMENT","LC_IDENTIFICATION","LC_ALL", 200 | "MXTX_LINK" }; 201 | for (unsigned int i = 0; i < sizeof pe / sizeof (char *); i++) { 202 | // xxx could go through ** environ, perhaps some day ? 203 | char * e = getenv(pe[i]); 204 | if (e) { 205 | int l = snprintf((char *)p + 2, sizeof pr.data - (p - pr.data) - 4, 206 | "e%s=%s", pe[i], e); 207 | p[0] = l / 256; p[1] = l % 256; 208 | p += l + 2; 209 | if ((p - pr.data) > isizeof pr.data - 100) // unlikely! 210 | die("Environment variables takes too much space"); 211 | } 212 | } 213 | if (argc == 0) { 214 | *p++ = '\0'; *p++ = '\001'; *p++ = 's'; // interactive shell 215 | } 216 | else { 217 | unsigned char * s = p; p += 3; 218 | for (int i = caa? 1: 0; i < argc; i++) { 219 | int l = strlen(argv[i]); 220 | if (p - pr.data >= isizeof pr.data - l - 4) 221 | die("Command line and env. variables take too much space"); 222 | memcpy(p, argv[i], l); 223 | p += l; *p++ = caa? '\0': ' '; 224 | } 225 | if (caa) s[2] = 'c'; 226 | else { p--; s[2] = 's'; } 227 | s[0] = (p - s - 2) / 256; s[1] = (p - s - 2) % 256; 228 | } 229 | // write whole command structure in one sweep 230 | (void)!write(3, pr.data, p - pr.data); 231 | BE; 232 | BB; 233 | size_t l = read(3, pr.data, sizeof mxtx_rshd_ident + 3); 234 | if (l < 8) 235 | die("could not read rshd ident"); 236 | if (memcmp(pr.data, mxtx_rshd_ident, sizeof mxtx_rshd_ident) != 0) 237 | die("server ident mismatch"); 238 | if (l < sizeof mxtx_rshd_ident + 3) 239 | l = read(3, pr.data + l, sizeof mxtx_rshd_ident + 3 - l); 240 | if (memcmp(pr.data + sizeof mxtx_rshd_ident, "\0\001" "a", 3) != 0) 241 | die("initial ack not received"); 242 | BE; 243 | if (G.tty > 0) { 244 | struct termios tio = G.saved_tio; 245 | // see ttydefaults.h and then compare w/ what other sw does here 246 | cfmakeraw(&tio); 247 | tio.c_cc[VMIN] = 1; 248 | tio.c_cc[VTIME] = 0; 249 | atexit(reset_tio); 250 | tcsetattr(0, TCSANOW, &tio); 251 | sigact(SIGWINCH, sighandler, 0); 252 | } 253 | else { 254 | sigact(SIGHUP, sighandler, 0); 255 | sigact(SIGINT, sighandler, 0); 256 | sigact(SIGQUIT, sighandler, 0); 257 | sigact(SIGTERM, sighandler, 0); 258 | } 259 | 260 | lpktread_init(&pr, 3); 261 | 262 | struct pollfd pfds[2]; 263 | pfds[0].fd = 3; pfds[0].events = POLLIN; 264 | pfds[1].fd = 0; pfds[1].events = POLLIN; 265 | int nfds = G.nostdin? 1: 2; 266 | while (1) { 267 | char ibuf[8192]; 268 | int n = poll(pfds, nfds, -1); 269 | (void)n; 270 | if (pfds[0].revents) { 271 | unsigned char * datap; 272 | int len; 273 | while ( (len = lpktread(&pr, &datap)) > 0) { 274 | if (datap[0] == '1') { 275 | xwritefully(1, (char *)datap + 1, len - 1); 276 | continue; 277 | } 278 | if (datap[0] == '2') { 279 | xwritefully(2, (char *)datap + 1, len - 1); 280 | continue; 281 | } 282 | if (datap[0] == 'a') { 283 | G.waitsigack = 0; 284 | G.mayhavesig = 1; 285 | continue; 286 | } 287 | if (datap[0] == 'x') { 288 | /**/ if (datap[1] == '1') { if (!isatty(1)) close(1); } 289 | else if (datap[1] == '2') { if (!isatty(2)) close(2); } 290 | continue; 291 | } 292 | if (datap[0] == 'e') { 293 | exit(datap[1]); // XXX handle exit code // 294 | } 295 | warn("drop unknown input %02x", datap[0]); 296 | continue; 297 | } 298 | if (len < 0) { 299 | if (datap != null) 300 | warn("read from mxtx failed:"); 301 | else 302 | warn("EOF from mxtx"); 303 | exit(7); 304 | } 305 | } 306 | if (pfds[1].revents) { 307 | int len = read(0, ibuf + 3, sizeof ibuf - 3); 308 | if (len <= 0) { 309 | if (len == 0) { 310 | // send eof // 311 | char msgbuf[3] = { 0, 1, 'x' }; 312 | xwritefully(3, msgbuf, 3); 313 | // pfds[1].fd = -1; // not needed anymore 314 | nfds = 1; 315 | } 316 | else die("read from stdin failed:"); 317 | } 318 | else { 319 | //for (int i=0; i 0) { 330 | if (G.sighup) { 331 | G.sighup = 0; 332 | struct winsize ws; 333 | if (ioctl(0, TIOCGWINSZ, &ws) < 0) { 334 | warn("getting window size failed"); 335 | } 336 | else { 337 | *p++ = '\0'; *p++ = '\005'; *p++ = 'w'; 338 | *p++ = ws.ws_col / 256; *p++ = ws.ws_col % 256; 339 | *p++ = ws.ws_row / 256; *p++ = ws.ws_row % 256; 340 | } 341 | } 342 | } 343 | else { 344 | if (G.sighup) { G.sighup = 0; 345 | *p++ = '\0'; *p++ = '\002'; *p++ = 's'; *p++ = RSH_SIGHUP; 346 | } 347 | if (G.sigint) { G.sigint = 0; 348 | *p++ = '\0'; *p++ = '\002'; *p++ = 's'; *p++ = RSH_SIGINT; 349 | } 350 | if (G.sigquit) { G.sigquit = 0; 351 | *p++ = '\0'; *p++ = '\002'; *p++ = 's'; *p++ = RSH_SIGQUIT; 352 | } 353 | if (G.sigterm) { G.sigterm = 0; 354 | *p++ = '\0'; *p++ = '\002'; *p++ = 's'; *p++ = RSH_SIGTERM; 355 | } 356 | } 357 | if (p != pr.data) { 358 | xwritefully(3, pr.data, p - pr.data); 359 | G.waitsigack = 1; 360 | } 361 | } 362 | } 363 | return 0; 364 | } 365 | 366 | static void sighandler(int sig) { 367 | G.mayhavesig = 1; 368 | switch (sig) { 369 | case SIGHUP: case SIGWINCH: G.sighup = 1; break; 370 | case SIGINT: G.sigint = 1; break; 371 | case SIGTERM: G.sigterm = 1; break; 372 | case SIGQUIT: G.sigquit = 1; break; 373 | } 374 | } 375 | -------------------------------------------------------------------------------- /src/mxtx-rsh.ch: -------------------------------------------------------------------------------- 1 | /* -*- mode: c; c-file-style: "stroustrup"; tab-width: 8; -*- 2 | * 3 | * Author: Tomi Ollila -- too ät iki piste fi 4 | * 5 | * Copyright (c) 2017 Tomi Ollila 6 | * All rights reserved 7 | * 8 | * Created: Sat 02 Sep 2017 18:24:44 EEST too 9 | * Last modified: Sat 26 Feb 2022 22:19:31 +0200 too 10 | */ 11 | 12 | // some common parts of mxtx-rsh and mxtx-rshd -- contains generated code 13 | 14 | #define MXTX_RSHC_IDENT "mxtxrsh2" 15 | const char mxtx_rshc_ident[8] = { 'm','x','t','x','r','s','h','2' }; 16 | const char mxtx_rshd_ident[8] = { 'm','x','t','x','r','s','h','3' }; 17 | 18 | 19 | // signal delivery. // expect ack // perhaps later 20 | #define RSH_SIGHUP 1 21 | #define RSH_SIGINT 2 22 | #define RSH_SIGQUIT 3 23 | #define RSH_SIGKILL 9 // self-delivered, if ever 24 | #define RSH_SIGUSR1 10 25 | #define RSH_SIGUSR2 12 26 | #define RSH_SIGPIPE 13 27 | #define RSH_SIGTERM 15 28 | #define RSH_SIGCONT 18 // self-delivered, if ever 29 | #define RSH_SIGSTOP 19 // ditto 30 | #define RSH_SIGWINCH 28 31 | -------------------------------------------------------------------------------- /src/mxtx-rshd.c: -------------------------------------------------------------------------------- 1 | #if 0 /* -*- mode: c; c-file-style: "stroustrup"; tab-width: 8; -*- 2 | set -euf; trg=${0##*''/}; trg=${trg%.c}; test ! -e "$trg" || rm "$trg" 3 | case ${1-} in '') set x -O2; shift; esac 4 | #case ${1-} in '') set x -ggdb; shift; esac 5 | set -x; exec ${CC:-gcc} -std=c99 "$@" -o "$trg" "$0" -L. -lmxtx -lutil 6 | exit $? 7 | */ 8 | #endif 9 | /* 10 | * $ mxtx-rshd.c $ 11 | * 12 | * Author: Tomi Ollila -- too ät iki piste fi 13 | * 14 | * Copyright (c) 2017 Tomi Ollila 15 | * All rights reserved 16 | * 17 | * Created: Sat 02 Sep 2017 18:42:37 EEST too 18 | * Last modified: Sat 02 Apr 2022 00:22:18 +0300 too 19 | */ 20 | 21 | #include "more-warnings.h" 22 | 23 | #define execvp(f,a) __xexecvp(f,a) // cheat constness... 24 | 25 | //#define _DEFAULT_SOURCE // defined in more-warnings.h 26 | #define _GNU_SOURCE // for some older linux environments 27 | 28 | #include 29 | #include 30 | //#include 31 | #include 32 | //#include 33 | //#include 34 | #include 35 | #include 36 | #include 37 | #include 38 | #if defined(__linux__) && __linux__ || defined(__CYGWIN__) && __CYGWIN__ 39 | #include 40 | #include 41 | #include // login_tty() 42 | #else 43 | #include // trial & error on freebsd 44 | #include 45 | #endif 46 | #include 47 | //#include <.h> 48 | #include 49 | 50 | #undef execvp 51 | void execvp(const char * file, const char ** argv); 52 | 53 | #include "mxtx-lib.h" 54 | 55 | #include "mxtx-rsh.ch" 56 | #define LPKTREAD_DATA_BUFFER_SIZE (65532 - 3) 57 | #include "lpktread.ch" 58 | 59 | #define null ((void*)0) 60 | 61 | #define BB { 62 | #define BE } 63 | 64 | const char * _prg_ident = "rshd: "; 65 | 66 | struct { 67 | pid_t pid; 68 | int cmd_fds; 69 | char env_pid[32]; 70 | } G; 71 | 72 | static void exec_cmd(unsigned int havetty, const char* file, const char** argv) 73 | { 74 | union { 75 | struct { 76 | int tty, pty; 77 | } t; 78 | struct { 79 | int sv[2], pv[2]; 80 | } p; 81 | } u; 82 | 83 | // warn("havetty: %08x", havetty); 84 | if (havetty) { 85 | struct winsize ws = { 86 | .ws_col = havetty >> 16, .ws_row = havetty & 0xffff 87 | }; 88 | //struct termios tio; 89 | //if (openpty(&pty, &tty, null, &tio, &ws) < 0) 90 | if (openpty(&u.t.pty, &u.t.tty, null, null, &ws) < 0) 91 | die("openpty:"); 92 | } 93 | else { 94 | if (socketpair(AF_UNIX, SOCK_STREAM, 0, u.p.sv) < 0) 95 | die("socketpair:"); 96 | if (pipe(u.p.pv) < 0) 97 | die("pipe:"); 98 | } 99 | if ( (G.pid = fork()) == 0) { 100 | // child // 101 | if (havetty) { 102 | close(u.t.pty); 103 | if (login_tty(u.t.tty) < 0) die("login_tty:"); 104 | } 105 | else { 106 | close(u.p.sv[0]); close(u.p.pv[0]); 107 | xmovefd(u.p.sv[1], 0); xdup2(0, 1); xmovefd(u.p.pv[1], 2); 108 | } 109 | execvp(file, argv); 110 | die("execvp %s:", file); 111 | } 112 | // parent // 113 | if (havetty) { 114 | close(u.t.tty); 115 | xmovefd(u.t.pty, 3); 116 | } 117 | else { 118 | close(u.p.sv[1]); close(u.p.pv[1]); 119 | xmovefd(u.p.sv[0], 3); xmovefd(u.p.pv[0], 4); 120 | } 121 | } 122 | 123 | 124 | static void wait_exit(void) ATTRIBUTE ((noreturn)); 125 | static void wait_exit(void) 126 | { 127 | for (int i = 0; i < 100; i++) { 128 | int status; 129 | // xxx should read input from 0, if any ... // 130 | pid_t pid = waitpid(G.pid, &status, 0); 131 | if (pid == G.pid) { 132 | // XXX encode to something peer can decode (more portable) 133 | // or check if this is just compatible... 134 | char msgbuf[5] = { 0, 3, 'e', status / 256 & 0xff, status & 0xff }; 135 | xwritefully(1, msgbuf, 5); 136 | break; 137 | } 138 | } 139 | exit(0); 140 | } 141 | 142 | 143 | static inline void pio(int fd, struct pollfd * pfd, char * buf, size_t buflen) 144 | { 145 | int chnl = fd + '0' - 2; 146 | int len = read(fd, buf + 3, buflen - 3); 147 | if (len <= 0) { 148 | if (len == 0 || pfd->revents & POLLNVAL /* closed by us */ 149 | || errno == EIO /* tty :O */) { 150 | // send eof, channel 1 or 2 // 151 | char msgbuf[4] = { 0, 2, 'x', chnl }; 152 | xwritefully(1, msgbuf, 4); 153 | if (--G.cmd_fds <= 0) 154 | wait_exit(); 155 | pfd->fd = -pfd->fd; 156 | } 157 | else die("XXX read(%d) (revents: %x):", fd, pfd->revents); 158 | } 159 | else { 160 | len += 1; 161 | buf[0] = len / 256; buf[1] = len % 256; buf[2] = chnl; 162 | xwritefully(1, buf, len + 2); 163 | } 164 | } 165 | 166 | static void socket_io(LPktRead * pr); 167 | 168 | int main(void) 169 | //int main(int argc, char * argv[]) // later perhaps debug options... 170 | { 171 | (void)!write(1, mxtx_rshd_ident, sizeof mxtx_rshd_ident); 172 | LPktRead pr; 173 | xreadfully(0, pr.data, sizeof mxtx_rshc_ident); 174 | if (memcmp(pr.data, mxtx_rshc_ident, sizeof mxtx_rshc_ident) != 0) 175 | die("client ident mismatch"); 176 | unsigned int tty = 0; 177 | snprintf(G.env_pid, sizeof G.env_pid, "MXTX_PID=%d", getpid()); 178 | putenv(G.env_pid); 179 | BB; 180 | char xenv[1024]; 181 | unsigned int xepos = 0; 182 | // xxx look man ssh for ideas what env vars set by me // 183 | lpktread_init(&pr, 0); 184 | while (1) { 185 | int len; 186 | unsigned char * data; 187 | while ((len = lpktread(&pr, &data)) == 0) {}; 188 | if (len < 0) // change to silent exit 189 | die("EOF (or fatal error)"); 190 | // data[len] = '\0'; warn("read len: %d: %s", len, data); // XXX debug 191 | switch (data[0]) { 192 | case 'e': 193 | if (xepos + len >= sizeof xenv - 2) // -2 inaccurate but safe 194 | warn("env buffer full; skipping adding new"); // unlikely 195 | // there is no point disallowing any env vars as long as 196 | // environment setting is not restricted by executed command line 197 | memcpy(xenv + xepos, data + 1, len - 1); 198 | xenv[xepos + len - 1] = '\0'; 199 | putenv(xenv + xepos); 200 | xepos += len + 1 - 1; 201 | break; 202 | case 't': // tty, if size > 0 203 | if (len == 5) { 204 | tty = data[1] << 24 | data[2] << 16 | data[3] << 8 | data[4]; 205 | if (tty == 0) tty = 0x00500018; // 80x24 206 | } 207 | break; 208 | case 'c': { 209 | if (len < 3) 210 | die("No command!"); 211 | #define MAX_ARGS 64 // copied from mxtx.c... 212 | const char * argv[MAX_ARGS]; 213 | int argc = 0; 214 | len -= 2; 215 | data++; 216 | if (data[len] != 0) 217 | die("Command buffer does not end with '\\0'"); 218 | int l = strlen((const char *)data); 219 | do { 220 | //warn("New command: argv[%d] = '%s' (%d bytes)", argc, data, l); 221 | argv[argc++] = (const char *)data; 222 | // warn("%d %d", len, l); 223 | if (l >= len) { 224 | argv[argc] = null; 225 | break; 226 | } 227 | data += (l + 1); 228 | len -= (l + 1); 229 | l = strlen((const char *)data); 230 | } while (argc < MAX_ARGS); 231 | if (argc == MAX_ARGS) 232 | die("Command '%s' has too many args", argv[0]); 233 | #undef MAX_ARGS 234 | exec_cmd(tty, argv[0], argv); 235 | } 236 | goto _cont; 237 | case 's': { 238 | const char * shell = getenv("SHELL"); 239 | const char * argv[4]; 240 | 241 | if (shell == null) shell = "/bin/sh"; 242 | char sbuf[12]; // with '8' gcc7 complained about truncation... 243 | if (len == 1) { 244 | char * p = strrchr(shell, '/'); 245 | // hax for shell launching wrappers which cannot handle 246 | // modified argv[0]: use as e.g. SHELL=/usr/local/bin//zsh 247 | if (p && p != shell && p[-1] == '/') { 248 | argv[0] = p+1; 249 | argv[1] = "-il"; 250 | argv[2] = null; 251 | } 252 | else { 253 | snprintf(sbuf, sizeof sbuf, "-%s", p? p+1: shell); 254 | argv[0] = sbuf; 255 | argv[1] = null; 256 | } 257 | } 258 | else { 259 | argv[0] = shell; 260 | argv[1] = "-c"; argv[2] = (char *)data + 1; argv[3] = null; 261 | } 262 | exec_cmd(tty, shell, argv); 263 | } 264 | goto _cont; 265 | default: 266 | warn("skipping unknown data %02x", data[0]); 267 | } 268 | } 269 | _cont: 270 | //signal(SIGCHLD, SIG_DFL); // seemed to inherit parent NOCLDWAIT ? // 271 | (void)!write(1, "\0\001" "a", 3); 272 | BE; 273 | char ibuf[8192]; 274 | if (tty) { 275 | struct pollfd pfds[2]; 276 | pfds[0].fd = 0; pfds[0].events = POLLIN; 277 | pfds[1].fd = 3; pfds[1].events = POLLIN; 278 | G.cmd_fds = 1; 279 | while (1) { 280 | int n = poll(pfds, 2, -1); 281 | (void)n; 282 | if (pfds[0].revents) socket_io(&pr); 283 | if (pfds[1].revents) pio(3, &pfds[1], ibuf, sizeof ibuf); 284 | } 285 | } else { // not tty 286 | struct pollfd pfds[3]; 287 | pfds[0].fd = 0; pfds[0].events = POLLIN; 288 | pfds[1].fd = 3; pfds[1].events = POLLIN; 289 | pfds[2].fd = 4; pfds[2].events = POLLIN; 290 | G.cmd_fds = 2; 291 | while (1) { 292 | int n = poll(pfds, 3, -1); 293 | (void)n; 294 | if (pfds[0].revents) socket_io(&pr); 295 | if (pfds[1].revents) pio(3, &pfds[1], ibuf, sizeof ibuf); 296 | if (pfds[2].revents) pio(4, &pfds[2], ibuf, sizeof ibuf); 297 | } 298 | } 299 | return 0; 300 | } 301 | 302 | static void socket_io(LPktRead * pr) 303 | { 304 | unsigned char * datap; 305 | int len; 306 | while ( (len = lpktread(pr, &datap)) > 0) { 307 | if (datap[0] == '0') { 308 | xwritefully(3, (char *)datap + 1, len - 1); 309 | continue; 310 | } 311 | if (datap[0] == 'w') { 312 | if (len == 5) { 313 | struct winsize ws = { 314 | .ws_col = datap[1] * 256 + datap[2], 315 | .ws_row = datap[3] * 256 + datap[4] 316 | }; 317 | if (ioctl(3, TIOCSWINSZ, &ws) < 0) 318 | warn("WINCH ioctl (%dx%x) failed:", ws.ws_col, ws.ws_row); 319 | } 320 | xwritefully(1, "\0\001" "a", 3); // ack, as requested 321 | continue; 322 | } 323 | if (datap[0] == 's') { 324 | if (len == 2) { 325 | int sig = 0; 326 | #define CASE(name) case RSH_ ## name: sig = name; break 327 | switch (datap[1]) { 328 | CASE (SIGHUP); 329 | CASE (SIGINT); 330 | CASE (SIGQUIT); 331 | CASE (SIGKILL); 332 | CASE (SIGUSR1); 333 | CASE (SIGUSR2); 334 | CASE (SIGPIPE); 335 | CASE (SIGTERM); 336 | CASE (SIGCONT); 337 | CASE (SIGSTOP); 338 | //CASE (SIGWINCH); 339 | } 340 | #undef CASE 341 | if (sig) kill(G.pid, sig); 342 | } 343 | xwritefully(1, "\0\001" "a", 3); // ack, as requested 344 | continue; 345 | } 346 | if (datap[0] == 'x') 347 | goto _eof; 348 | 349 | warn("drop unknown cmd 0x%02x", datap[0]); 350 | } 351 | 352 | if (len == 0) 353 | return /*0*/; 354 | 355 | if (datap != null) 356 | warn("read from rsh client failed:"); 357 | _eof: 358 | // error or eof // 359 | if (shutdown(3, SHUT_WR) < 0) 360 | close(3); 361 | return /*1*/; 362 | } 363 | -------------------------------------------------------------------------------- /src/mxtx-socksproxy.c: -------------------------------------------------------------------------------- 1 | #if 0 /* -*- mode: c; c-file-style: "stroustrup"; tab-width: 8; -*- 2 | set -euf; trg=${0##*''/}; trg=${trg%.c}; test ! -e "$trg" || rm "$trg" 3 | uname=`exec uname` 4 | case $uname in *WIN*) echo $0: does not work on $uname; exit 0 ;; esac 5 | WARN="-Wall -Wstrict-prototypes -Winit-self -Wformat=2" # -pedantic 6 | WARN="$WARN -Wcast-align -Wpointer-arith " # -Wfloat-equal #-Werror 7 | WARN="$WARN -Wextra -Wwrite-strings -Wcast-qual -Wshadow" # -Wconversion 8 | WARN="$WARN -Wmissing-include-dirs -Wundef -Wbad-function-cast -Wlogical-op" 9 | WARN="$WARN -Waggregate-return -Wold-style-definition" 10 | WARN="$WARN -Wmissing-prototypes -Wmissing-declarations -Wredundant-decls" 11 | WARN="$WARN -Wnested-externs -Winline -Wvla -Woverlength-strings" 12 | case ${1-} in '') set x -O2; shift; esac 13 | #case ${1-} in '') set x -ggdb; shift; esac 14 | set -x; exec ${CC:-gcc} -std=c99 $WARN "$@" -o "$trg" "$0" -L. -lmxtx # -flto 15 | exit $? 16 | */ 17 | #endif 18 | /* 19 | * $ mxtx-socksproxy.c $ 20 | * 21 | * Author: Tomi Ollila -- too ät iki piste fi 22 | * 23 | * Copyright (c) 2017 Tomi Ollila 24 | * All rights reserved 25 | * 26 | * Created: Sun 20 Aug 2017 22:07:17 EEST too 27 | * Last modified: Mon 20 Feb 2023 21:33:11 +0200 too 28 | */ 29 | 30 | #if defined(__linux__) && __linux__ || defined(__CYGWIN__) && __CYGWIN__ 31 | #define _DEFAULT_SOURCE // for newer linux environments 32 | #define _POSIX_SOURCE // for some older linux environments 33 | #endif 34 | 35 | #include 36 | #include 37 | #include 38 | #include 39 | #include 40 | #include 41 | #include 42 | #include 43 | #include 44 | #include 45 | #include 46 | #include 47 | #include 48 | #include 49 | #include 50 | #include 51 | 52 | // for xinet_connect() 53 | #include 54 | #include 55 | 56 | #include "mxtx-lib.h" 57 | 58 | #define null ((void*)0) 59 | 60 | #define DBG 1 61 | #if DBG 62 | #define dprintf(...) fprintf(stderr, __VA_ARGS__) 63 | #define dprintf0(...) do { } while (0) 64 | #else 65 | #define dprintf(...) do { } while (0) 66 | #define dprintf0(...) do { } while (0) 67 | #endif 68 | 69 | const char * _prg_ident = "mxtx-socksproxy: "; 70 | 71 | static struct { 72 | char * network; 73 | char pidbuf[16]; 74 | pid_t ppid; // note: known -Wpadded warning... // 75 | struct timeval stv; 76 | sigjmp_buf restart_env; 77 | } G; 78 | 79 | #define LINEREAD_DATA_BUFFER_SIZE 4096 80 | #include "lineread.ch" 81 | 82 | static int find_dest(const char * host, int hostlen, 83 | const char ** net, const char ** addr) 84 | { 85 | const char * p = G.network; 86 | //write(1, p, 96); exit(0); 87 | 88 | const unsigned char * s = (const unsigned char *)p; 89 | p = (const char *)s + 1; 90 | dprintf0("link: '%s'\n", p); 91 | s += (int)s[0] + 2; 92 | while (1) { 93 | int len1 = s[0]; 94 | int len2 = s[1]; 95 | if (len1 == 0) { 96 | if (len2 == 0) break; 97 | p = (const char *)s + 2; 98 | dprintf0("link: '%s'\n", p); 99 | s += len2 + 3; 100 | continue; 101 | } 102 | s += 2; 103 | dprintf0("n: %s -- %d %d %s %s\n", p, len1, len2, s, s + len1 + 1); 104 | if (memcmp(host, s, hostlen + 1) == 0) { 105 | *net = p; 106 | *addr = (const char *)(s + len1 + 1); 107 | return len2; 108 | } 109 | s += len1 + len2 + 2; 110 | } 111 | return 0; 112 | } 113 | 114 | static void init(int argc, char * argv[]) 115 | { 116 | if (argc < 2) 117 | die("\nUsage: %s link [link...]\n\n" 118 | " '0' is good default link\n\n", argv[0]); 119 | #define INITIAL_ALLOC_SIZE 262144 // expect mmap, let's see what realloc does 120 | G.network = malloc(INITIAL_ALLOC_SIZE); 121 | if (G.network == null) die("out of memory"); 122 | char * p = G.network; 123 | for (int i = 1; argv[i]; i++) { 124 | if (p - G.network > INITIAL_ALLOC_SIZE - 1024) { 125 | warn("too much input data"); // make better msg if really happens 126 | break; 127 | } 128 | int sd; 129 | if (argv[i][0] == '\0' || (argv[i][1] == '\0' && 130 | (argv[i][0] == '/' || argv[i][0] == '.'))) { 131 | const char * home = getenv("HOME"); 132 | if (home == null) continue; // unlikely 133 | char fn[4096]; 134 | if ((unsigned)snprintf(fn, sizeof fn, 135 | "%s/.local/share/mxtx/hosts-to-proxy", 136 | home) >= sizeof fn) 137 | continue; // unlikely 138 | sd = open(fn, O_RDONLY); 139 | if (sd < 0) { 140 | warn("Cannot open %s:", fn); 141 | continue; 142 | } 143 | if (argv[i][0] != '\0') argv[i][0] = '\0'; 144 | } 145 | else { 146 | sd = connect_unix_stream_mxtx_socket(argv[i], ""); 147 | if (sd < 0) continue; 148 | (void)!write(sd, "\0\0\0\024~cat\0hosts-to-proxy\0", 24); 149 | } 150 | // we know argv[i] maxlen is less than 108 151 | p += snprintf(p, 256, "%c%s", (int)strlen(argv[i]), argv[i]) + 1; 152 | fprintf(stderr, "link: '%s'\n", argv[i]); 153 | LineRead lr; 154 | lineread_init(&lr, sd); 155 | int ln = 0; 156 | while (1) { 157 | char * dp; // put outside loop if cared read() return values... 158 | int ll = lineread(&lr, &dp); 159 | if (ll <= 0) { if (ll == 0) continue; else break; } 160 | ln++; 161 | while (isspace(*dp)) dp++; 162 | if (*dp == '#') continue; // "comment" line 163 | char * ip = strpbrk(dp, " \t"); 164 | if (ip == null) { 165 | warn("%s:%d: no space separator", argv[i], ln); 166 | continue; 167 | } 168 | if (dp == ip) { 169 | warn("%s:%d: hostname empty", argv[i], ln); 170 | continue; 171 | } 172 | int hnlen = ip - dp; 173 | *ip = '\0'; 174 | if (hnlen > 250) { 175 | warn("%s:%d: hostname '%s' too long", argv[i], ln, dp); 176 | continue; 177 | } 178 | while (isspace(*++ip)) {}; 179 | char * ep = ip; 180 | // fix multiple spaces, laita pituus talteen 181 | while (!isspace(*ep) && *ep != '\0') ep++; 182 | if (ip == ep) { 183 | warn("%s:%d: ip address empty", argv[i], ln); 184 | continue; 185 | } 186 | int iplen = ep - ip; 187 | *ep = '\0'; 188 | if (iplen > 42) { 189 | warn("%s%d: ip address '%s' too long", argv[i], ln, ip); 190 | continue; 191 | } 192 | if (! isalnum(dp[0])) // check "outcomments" 193 | continue; 194 | p += snprintf(p, 512, "%c%c%s%c%s", hnlen, iplen, dp, 0, ip) + 1; 195 | fprintf(stderr, " %s: '%s'\n", dp, ip); 196 | } 197 | *p++ = '\0'; // end link with zero-length field (no second) 198 | close(sd); 199 | } 200 | fprintf(stderr, "\n"); 201 | *p++ = '\0'; // end list w/ double \0's 202 | G.network = realloc(G.network, p - G.network); 203 | if (G.network == null) die("Out of memory"); // let system clear it... 204 | 205 | #if DBG // test scan... 206 | (void)find_dest("", 0, null, null); 207 | #endif 208 | 209 | struct sockaddr_un saddr; 210 | #if defined(__linux__) && __linux__ 211 | fill_sockaddr_un(&saddr, "%c/tmp/user-%d/un-s-1080", 0, getuid()); 212 | #else 213 | char * xdgrd = getenv("XDG_RUNTIME_DIR"); 214 | if (xdgrd) 215 | fill_sockaddr_un(&saddr, "%s/un-s-1080", xdgrd); 216 | else { 217 | uid_t uid = getuid(); 218 | char dir[64]; 219 | (void)snprintf(dir, sizeof dir, "/tmp/user-%d", uid); 220 | (void)mkdir(dir, 0700); 221 | fill_sockaddr_un(&saddr, "/tmp/user-%d/un-s-1080", uid); 222 | } 223 | #endif 224 | int sd = xbind_listen_unix_socket(&saddr, SOCK_STREAM); 225 | xmovefd(sd, 3); 226 | } 227 | 228 | static void restart_sighandler(int sig) 229 | { 230 | (void)sig; 231 | if (getpid() != G.ppid) return; 232 | close(3); 233 | warn("Restarting..."); 234 | siglongjmp(G.restart_env, 1); 235 | die("not reached"); 236 | } 237 | 238 | static void childsexit_sighandler(int sig) 239 | { 240 | (void)sig; 241 | pid_t pid = getpid(); 242 | if (pid == G.ppid) return; 243 | warn("Process %d exiting", pid); 244 | exit(0); 245 | } 246 | 247 | 248 | // echo 50 | tr 50 '\005\000' | nc -l 1080 | od -tx1 249 | 250 | static void start(void); 251 | static void io(int ifd, int ofd); 252 | 253 | extern char ** environ; 254 | int main(int argc, char * argv[]) 255 | { 256 | seterr_linebuf(); 257 | init(argc, argv); 258 | sigact(SIGCHLD, SIG_IGN, SA_NOCLDWAIT); 259 | 260 | G.ppid = getpid(); 261 | if (sigsetjmp(G.restart_env, 1)) { 262 | execve(argv[0], argv, environ); 263 | die("not reached"); 264 | } 265 | sigact(SIGUSR1, restart_sighandler, 0); 266 | sigact(SIGUSR2, childsexit_sighandler, 0); 267 | 268 | while (1) { 269 | int sd = accept(3, null, 0); 270 | if (sd < 0 && errno == EINTR) 271 | continue; 272 | if (! checkpeerid(sd)) { 273 | close(sd); 274 | continue; 275 | } 276 | if (fork()) { 277 | close(sd); 278 | continue; 279 | } 280 | // child of fork() 281 | xmovefd(sd, 3); 282 | break; 283 | } 284 | // child of fork() continues 285 | start(); 286 | 287 | struct pollfd pfds[2]; 288 | pfds[0].fd = 3; pfds[1].fd = 4; 289 | pfds[0].events = pfds[1].events = POLLIN; 290 | 291 | while (1) { 292 | if (poll(pfds, 2, -1) <= 0) { 293 | warn("unexpected poll() return without any input available"); 294 | sleep(1); 295 | } 296 | if (pfds[0].revents) io(3, 4); 297 | if (pfds[1].revents) io(4, 3); 298 | } 299 | return 0; 300 | } 301 | 302 | static float elapsed(void) { 303 | struct timeval tv; 304 | gettimeofday(&tv, null); 305 | if (tv.tv_usec < G.stv.tv_usec) { 306 | tv.tv_sec--; 307 | tv.tv_usec += 1000000; 308 | } 309 | return (tv.tv_sec - G.stv.tv_sec) + 310 | (float)(tv.tv_usec - G.stv.tv_usec) / 1000000; 311 | } 312 | 313 | static int xinet_connect(const char * addr, int iport /*, bool nonblock */) 314 | { 315 | // copy from mxtx.c // (gener|lib)ify some day (w/ v6)... 316 | // in the future name resolving (using 9.9.9.9 perhaps) perhaps 317 | // basic ipv4 style for the time being... 318 | in_addr_t iaddr = inet_addr(addr); 319 | if (iaddr == INADDR_NONE) { 320 | die("%s: invalid address", addr); 321 | } 322 | if (iport < 1 || iport > 65535) { 323 | die("%d: invalid port", iport); 324 | } 325 | int sd = socket(AF_INET, SOCK_STREAM, 0); 326 | struct sockaddr_in ia = { 327 | .sin_family = AF_INET, 328 | .sin_port = htons(iport), 329 | .sin_addr = { .s_addr = iaddr } 330 | }; 331 | if (connect(sd, (struct sockaddr *)&ia, sizeof ia) < 0) { 332 | close(sd); 333 | die("Connection to %s:%d failed:", addr, iport); 334 | } 335 | return sd; 336 | } 337 | 338 | static void may_serve_index_file_request(const char * host, char * rbuf); 339 | 340 | static void start(void) 341 | { 342 | char buf[512]; 343 | char * p; 344 | unsigned char * ubuf = (unsigned char *)buf; 345 | int port; 346 | gettimeofday(&G.stv, null); 347 | snprintf(G.pidbuf, sizeof G.pidbuf, "sp-%d: ", getpid()); 348 | _prg_ident = G.pidbuf; 349 | /* 350 | * In this program we can do things simple and expect system to return 351 | * enough data -- and if not, then drop it (we'll see how well this flies) 352 | */ 353 | alarm(10); 354 | int len = read(3, buf, 260); 355 | if (len < 3) 356 | die("Could not read min 3 octets of data (read returned %d)", len); 357 | 358 | const char * net = null; 359 | 360 | // https://www.ietf.org/rfc/rfc1928.txt 361 | // https://en.wikipedia.org/wiki/SOCKS 362 | 363 | if (buf[0] == 5) { // socks5 first // 364 | if (len != ubuf[1] + 2) 365 | die("Unexpected number of octets of input data(%d != %d)", 366 | len, ubuf[1] + 2); 367 | (void)!write(3, "\005", 2); // reply 05 00 (no authentication required) 368 | 369 | // we trust such a small amout of data can be read on one call... 370 | len = read(3, buf, 5 + 255 + 2); // VER CMD RV ATYP DST.ADDR DST.PORT 371 | if (len < 10) 372 | die("Minimum socks5 request has 10 octets (got %d)", len); 373 | if (buf[1] != 0x01) 374 | die("Request not CONNECT (0x01) (was %02x)", ubuf[1]); 375 | 376 | strcpy(buf + 304, ":connect"); 377 | p = buf + 313; 378 | dprintf0("type: %d\n", buf[3]); 379 | if (buf[3] == 0x03) { // domain name 380 | int hlen = ubuf[4]; 381 | int rest = hlen + 7; 382 | if (len != rest) 383 | die("Name socks5 request not %d octets (was %d octets)", 384 | rest, len); 385 | port = ubuf[rest - 2] * 256 + ubuf[rest - 1]; 386 | ubuf[rest - 2] = '\0'; 387 | // the following function may not return (see more in definition) 388 | may_serve_index_file_request(buf + 5, p); 389 | const char * addr; 390 | int al = find_dest(buf + 5, hlen, &net, &addr); 391 | if (al) p += sprintf(p, "%s", addr) + 1; // p was buf + 313 392 | else p += sprintf(p, "%s", buf + 5) + 1; // for warn() below 393 | } 394 | else if (buf[3] == 0x01) { // ipv4 address 395 | warn("Sorry, forgot to complete ipv4 address type support\n" 396 | "\tALL_PROXY=socks5h://127.0.0.1:1080 could help..."); 397 | if (len != 10) 398 | die("IPv4 socks5 request not 10 octets (was %d octets)", len); 399 | port = ubuf[8] * 256 + ubuf[9]; 400 | p += sprintf(p, "%u.%u.%u.%u", ubuf[4],ubuf[5],ubuf[6],ubuf[7]) + 1; 401 | } 402 | else if (buf[3] == 0x04) { // ipv6 address 403 | warn("Sorry, forgot to complete ipv6 address type support\n" 404 | "\tALL_PROXY=socks5h://127.0.0.1:1080 could help..."); 405 | if (len != 22) 406 | die("IPv6 socks5 request not 22 octets (was %d octets)", len); 407 | port = ubuf[20] * 256 + ubuf[21]; 408 | p += sprintf(p, "%x:%x:%x:%x:%x:%x:%x:%x", 409 | ubuf[4] * 256 + ubuf[5], ubuf[6] * 256 + ubuf[7], 410 | ubuf[8] * 256 + ubuf[9], ubuf[10] * 256 + ubuf[11], 411 | ubuf[12] * 256 + ubuf[13], ubuf[14] * 256 + ubuf[15], 412 | ubuf[16] * 256 + ubuf[17], ubuf[18] * 256 + ubuf[19]) 413 | + 1; 414 | } 415 | else die("Unknown socks5 request %02x", buf[3]); 416 | } 417 | else if (buf[0] == 4) { 418 | die("Socks4 support to be implemented"); 419 | } 420 | else die ("Unknown socks version (%d) request", buf[0]); 421 | alarm(0); 422 | if (net == null) { 423 | warn("'%s'(%d) not in any configuration", buf + 313, port); 424 | (void)!write(3, "\005\004\0\001" "\0\0\0\0" "\0", 10); 425 | exit(0); 426 | } 427 | if (buf[0] == 5 && buf[3] == 0x03) { 428 | warn("Connecting %s %s:%d via %s", buf + 5, buf + 313, port, net); 429 | } 430 | else 431 | warn("Connecting %s:%d via %s", buf + 313, port, net); // XXX loglevel 432 | 433 | int sd; 434 | if (net[0] == '\0') { 435 | sd = xinet_connect(buf + 313, port); 436 | } 437 | else { 438 | sd = connect_unix_stream_mxtx_socket(net, ""); 439 | if (sd < 0) 440 | die("Cannot connect to mxtx socket '%s':", net); 441 | 442 | len = p - buf - 304 + sprintf(p, "%d", port) + 1; 443 | ubuf[300] = ubuf[301] = 0; // protocol version 444 | ubuf[302] = len / 256; ubuf[303] = len % 256; // msg length 445 | (void)!write(sd, buf + 300, len + 4); 446 | if (read(sd, buf, 1) != 1) 447 | die("Did not get reply from mxtx socket '%s' (%s:%d):", 448 | net, buf + 313, port); 449 | if (buf[0] != 0) { 450 | warn("Nonzero (%d) reply code for '%s:%d'", buf[0],buf + 313,port); 451 | switch (ubuf[0]) { 452 | case ECONNREFUSED: // check matching errno's on every OS 453 | (void)!write(3, "\005\005\0\001" "\0\0\0\0" "\0", 10); break; 454 | default: 455 | (void)!write(3, "\005\001\0\001" "\0\0\0\0" "\0", 10); break; 456 | } 457 | exit(0); 458 | } 459 | } 460 | // request granted, ipv4 address 10.0.0.7, port 16128 461 | (void)!write(3, "\005\000\0\001" "\012\0\0\007" "\077", 10); 462 | 463 | // examine possibility to fdpass socket fd ... // 464 | xmovefd(sd, 4); 465 | } 466 | 467 | static void io(int ifd, int ofd) 468 | { 469 | char buf[8192]; 470 | int l = read(ifd, buf, sizeof buf); 471 | dprintf0("io: %d %d > %d", ifd, l, ofd); 472 | if (l <= 0) { 473 | // hmm, econnreset... 474 | if (l == 0 || errno == ECONNRESET) { 475 | float tr = elapsed(); 476 | warn("EOF from %d (%.3f s). Exiting.", ifd, tr); 477 | exit(0); 478 | } 479 | // do we need EINTR handling. possibly not, (as not blocking) // 480 | die("read from %d failed:", ifd); 481 | } 482 | int wl = write(ofd, buf, l); 483 | if (wl != l) { 484 | if (wl < 0) 485 | die("write to %d failed:", ofd); 486 | else 487 | die("short write to %d (%d < %d)", ofd, wl, l); 488 | } 489 | } 490 | 491 | // this function breaks the event loop idea in the main() function, 492 | // later this whole thing may be refactored, but initially this serves 493 | // the purpose 494 | static void may_serve_index_file_request(const char * host, char * rbuf) 495 | { 496 | char c; 497 | if (sscanf(host, "index-%32[^./].htm%1[l]%1s", rbuf, &c, &c) != 2) 498 | return; 499 | // add code to fetch index.html from remove and spiff some output 500 | // not the nicest way to handle this -- refactoring may be required 501 | // when more features are added to this program... 502 | alarm(0); 503 | 504 | warn("Loading index.html from %s", rbuf); 505 | 506 | // some duplicate code! 507 | int sd = connect_unix_stream_mxtx_socket(rbuf, ""); 508 | if (sd < 0) 509 | // xxx create better (i.e. 'a') reply page perhaps 510 | die("Cannot connect to mxtx socket '%s':", rbuf); 511 | 512 | (void)!write(sd, "\0\0\0\020~cat\0index.html\0", 20); 513 | 514 | // XXX hardcoded socks5 reply 515 | // request granted, ipv4 address 10.0.0.7, port 16128 (^^^ duplicated ^^^) 516 | (void)!write(3, "\005\000\0\001" "\012\0\0\007" "\077", 10); 517 | alarm(10); 518 | char buf[4096]; 519 | (void)!read(3, buf, sizeof buf); // and discard it // 520 | #define reply ("HTTP/1.1 200 OK\r\n" "Connection: close\r\n" "\r\n") 521 | (void)!write(3, reply, sizeof reply - 1); 522 | int len; 523 | while ((len = read(sd, buf, sizeof buf)) > 0) 524 | (void)!write(3, buf, len); 525 | close(3); 526 | close(sd); 527 | exit(0); 528 | } 529 | -------------------------------------------------------------------------------- /termtower-tmpl.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | # 3 | # $ termtower-tmpl.sh $ 4 | # 5 | # Author: Tomi Ollila -- too ät iki piste fi 6 | # 7 | # This file has been put into the public domain. 8 | # 9 | # Created: Tue 05 Sep 2017 22:44:11 EEST too 10 | # Last modified: Fri 05 Apr 2019 22:17:39 +0300 too 11 | 12 | # move current graphical terminal window to a position and open 13 | # two more terminal windows below that. copy and add features. 14 | 15 | case ${BASH_VERSION-} in *.*) set -o posix; shopt -s xpg_echo; esac 16 | case ${ZSH_VERSION-} in *.*) emulate ksh; esac 17 | 18 | set -euf 19 | #set -x 20 | 21 | decor_height=20 22 | font_height=15 23 | rows=20 24 | 25 | xpos=77 26 | ypos=20 27 | 28 | distance=$(( decor_height + rows * font_height )) 29 | 30 | saved_IFS=$IFS; readonly saved_IFS 31 | 32 | warn () { printf '%s\n' "$*"; } >&2 33 | die () { printf '%s\n' "$*"; exit 1; } >&2 34 | 35 | x () { printf '+ %s\n' "$*" >&2; "$@"; } 36 | x_env () { printf '+ %s\n' "$*" >&2; env "$@"; } 37 | x_eval () { printf '+ %s\n' "$*" >&2; eval "$*"; } 38 | x_exec () { printf '+ %s\n' "$*" >&2; exec "$@"; die "exec '$*' failed"; } 39 | 40 | find_term () { 41 | for term; do command -v $term >/dev/null && return 0 || :; done 42 | die 'No known X terminal program (add yours ?)' 43 | } 44 | 45 | if test "${DISPLAY-}" 46 | then 47 | find_term urxvt xterm 48 | newterm () { x=$1 y=$2; shift 2; $term -g 80x$rows+$x+$y "$@" & } 49 | 50 | elif case `exec uname` in *CYGWIN*) true ;; *) false ;; esac 51 | then 52 | command -v mintty >/dev/null || 53 | die "'mintty' not available (add yours?)" 54 | term=mintty 55 | newterm () { x=$1 y=$2; shift 2; $term -s 80,$rows -p $x,$y "$@" & } 56 | else 57 | die 'No known graphical environment (add yours ?)' 58 | fi 59 | 60 | printf "\033[8;$rows;80"t # resize to 80x$rows 61 | printf "\033[3;$xpos;$ypos"t # move to position ${xpos}x${ypos} 62 | 63 | newterm $xpos $(( ypos + distance )) 64 | newterm $xpos $(( ypos + distance * 2 )) 65 | 66 | # Local variables: 67 | # mode: shell-script 68 | # sh-basic-offset: 8 69 | # sh-indentation: 8 70 | # tab-width: 8 71 | # End: 72 | # vi: set sw=8 ts=8 73 | -------------------------------------------------------------------------------- /tst/multi-strace.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | # 3 | # $ multi-strace.sh $ 4 | # 5 | # Author: Tomi Ollila -- too ät iki piste fi 6 | # 7 | # Copyright (c) 2021 Tomi Ollila 8 | # All rights reserved 9 | # 10 | # Created: Thu 27 May 2021 21:36:22 EEST too 11 | # Last modified: Fri 28 May 2021 19:25:00 +0300 too 12 | 13 | case ${BASH_VERSION-} in *.*) set -o posix; shopt -s xpg_echo; esac 14 | case ${ZSH_VERSION-} in *.*) emulate ksh; esac 15 | 16 | set -euf # hint: sh -x thisfile [args] to trace execution 17 | 18 | LANG=C LC_ALL=C; export LANG LC_ALL; unset LANGUAGE 19 | 20 | die () { printf '%s\n' "$@"; exit 1; } >&2 21 | 22 | x_bg () { printf '+ %s\n' "$*" >&2; "$@" & } 23 | 24 | test $# -gt 0 || die '' "Usage: ${0##*/} names (e.g. '.mxtx' 'rsync')" '' \ 25 | "Notes: 'pgrep' behaves like 'grep -F'. May need root..." '' 26 | 27 | for arg 28 | do 29 | echo 30 | pgrep -a "$arg" | while read pid rest 31 | do 32 | trap wait 0 33 | echo $pid -- $rest 34 | x_bg strace -tt -o strace-$arg-$pid -p $pid 35 | done & 36 | done 37 | sleep 1 38 | echo 39 | wait 40 | 41 | 42 | # Local variables: 43 | # mode: shell-script 44 | # sh-basic-offset: 8 45 | # tab-width: 8 46 | # End: 47 | # vi: set sw=8 ts=8 48 | -------------------------------------------------------------------------------- /tst/test-disp-in-emacs: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | :; set -eufx; test -d $HOME/.local/share/mxtx 3 | :; exec "${EMACS:-emacs}" --debug-init -q --load "$0" "$@"; exit 4 | 5 | (setq initial-buffer-choice nil 6 | inhibit-startup-screen t) 7 | 8 | (defun run (name &rest cmd-args) 9 | (let* ((bn (concat "*" name "*")) 10 | (buf (generate-new-buffer bn))) 11 | (switch-to-buffer buf) 12 | (insert "$ " (mapconcat #'identity cmd-args " ") "\n") 13 | ;; "env" since start-process looks exec-path *only* :O 14 | (apply 'start-process name buf "env" cmd-args) 15 | (sit-for 0.2) 16 | ;;(goto-char (point-min)) ;;(redisplay) 17 | ;;(goto-char (point-max)) ;;(redisplay) 18 | )) 19 | 20 | (split-window-below) 21 | (split-window-below) 22 | (balance-windows) 23 | (run "mxtx0" "./ioio.pl" "/" "./mxtx -s" "/" "./mxtx -ct") 24 | (other-window 1) 25 | (run "mxtx1" "./ioio.pl" "." "./mxtx -s~" "." "./mxtx -cu") 26 | (other-window 1) 27 | (sleep-for 0 200) 28 | ;;(split-window-below) 29 | (run "mxtx-sb" "./mxtx-socksproxy" "t" "u" "" "/") 30 | ;;(other-window 1) 31 | 32 | ;; Local Variables: 33 | ;; mode: emacs-lisp 34 | ;; End: 35 | -------------------------------------------------------------------------------- /tst/test-strace-wrapper.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | # -*- mode: shell-script; sh-basic-offset: 8; tab-width: 8 -*- 3 | # $ test-strace-wrapper.sh $ 4 | 5 | # copy this to ~/.local/share/mxtx/ to a name of a program you 6 | # want to strace (before that rename the prg w/ -st suffix). 7 | # the ':::' as first argument (look code below) does that, too... 8 | 9 | # add options at will 10 | 11 | set -euf 12 | 13 | # installer ( e.g. enter ./test-strace-wrapper.sh ':::' mxtx-rshd ) 14 | if test "${1-}" = ':::' 15 | then 16 | set -x 17 | mv "$HOME"/.local/share/mxtx/"$2" "$HOME"/.local/share/mxtx/"$2"-st 18 | exec cp "$0" "$HOME"/.local/share/mxtx/"$2" 19 | fi 20 | 21 | bn=${0##*/} 22 | set -x 23 | cd "$HOME"/.local/share/mxtx/ ### XXX changes cwd XXX ### 24 | # first of the uncommented commands below is active 25 | #exec strace -f ./"$bn"-st "$@" 26 | exec strace -s 256 ./"$bn"-st "$@" 27 | exec ./"$bn"-st "$@" 28 | -------------------------------------------------------------------------------- /version.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | # -*- mode: shell-script; sh-basic-offset: 8; tab-width: 8 -*- 3 | # $ version.sh $ 4 | # 5 | # Author: Tomi Ollila -- too ät iki piste fi 6 | # 7 | # Copyright (c) 2017 Tomi Ollila 8 | # All rights reserved 9 | # 10 | # Created: Sun 12 Nov 2017 17:34:40 EET too 11 | # Last modified: Sat 26 Feb 2022 18:05:08 +0200 too 12 | 13 | case ${BASH_VERSION-} in *.*) set -o posix; shopt -s xpg_echo; esac 14 | case ${ZSH_VERSION-} in *.*) emulate ksh; esac 15 | 16 | set -euf 17 | 18 | version_num=3.0 19 | 20 | prev_commit=bb3232b734db4a26be2a375a39c0240fdb340af1 21 | 22 | LANG=C LC_ALL=C export LANG LC_ALL; unset LANGUAGE 23 | 24 | if test -e .git # || test -e ../.git 25 | then 26 | s () { set -- `exec git --no-pager log -1 --abbrev=7 --pretty='%h %ci'` 27 | ch=$1 cd=$2; }; s 28 | cn=`git --no-pager log --oneline $prev_commit..HEAD | wc -l` 29 | test "`exec git status --porcelain -uno`" && m=-mod || m= 30 | test "$cn" && cn=$((cn - 1)) || cn=9999 31 | echo $version_num-$cn-g$ch-$cd$m 32 | exit 33 | fi 34 | 35 | IFS=/ 36 | set -- $PWD '' 37 | for s 38 | do case $s in mxtx-20[0-9][0-9]-[0-9][0-9]-[0-9][0-9]-g*) break; esac 39 | done 40 | 41 | if test "$s" 42 | then 43 | IFS=- 44 | set -- $s 45 | echo $version_num-$5-$2-$3-$4 46 | else 47 | exec date +$version_num-%Y-%m-%d 48 | fi 49 | --------------------------------------------------------------------------------