├── LICENSE.txt ├── README.md ├── contrib ├── README.md ├── tio-limit-ab678e6c882e19.patch └── tio-noprefix-bfefd04b5567ba2b5.patch └── src ├── Makefile ├── Makefile.android.aarch64 ├── client.cc ├── external ├── aes.cc ├── aes.h ├── sha512.cc └── sha512.h ├── misc.cc ├── misc.h ├── net.cc ├── net.h ├── pcwrap.cc ├── pcwrap.h ├── pscsh.cc ├── pty.cc ├── pty.h ├── pty98.cc ├── script_ └── helloworld └── server.cc /LICENSE.txt: -------------------------------------------------------------------------------- 1 | /* 2 | * This file is part of port shell crypter (psc). 3 | * 4 | * (C) 2006-2021 by Sebastian Krahmer, 5 | * sebastian [dot] krahmer [at] gmail [dot] com 6 | * 7 | * psc is free software: you can redistribute it and/or modify 8 | * it under the terms of the GNU General Public License as published by 9 | * the Free Software Foundation, either version 3 of the License, or 10 | * (at your option) any later version. 11 | * 12 | * psc is distributed in the hope that it will be useful, 13 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 14 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 | * GNU General Public License for more details. 16 | * 17 | * You should have received a copy of the GNU General Public License 18 | * along with psc. If not, see . 19 | */ 20 | 21 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | PortShellCrypter -- PSC 2 | ======================= 3 | 4 | This project - as well as its sister project [crash](https://github.com/stealth/crash) - belongs 5 | to my anti-censorship tool-set that allows to setup fully working encrypted shells and TCP/UDP 6 | forwarding in hostile censoring environments. It is also useful for forensics to dump data from 7 | devices via UART or adb when no other means are available. 8 | 9 | [![asciicast](https://asciinema.org/a/383043.svg)](https://asciinema.org/a/383043) 10 | *DNS lookup and SSH session forwarded across an UART connection to a Pi* 11 | 12 | PSC allows to e2e encrypt shell sessions, single- or multip-hop, being 13 | agnostic of the underlying transport, as long as it is reliable and can send/receive 14 | Base64 encoded data without modding/filtering. Along with the e2e pty that 15 | you receive (for example inside a port-shell), you can forward TCP and UDP 16 | connections, similar to OpenSSH's `-L` parameter. This works transparently 17 | and without the need of an IP address assigned locally at the starting 18 | point. This allows forensicans and pen-testers to create network connections 19 | for example via: 20 | 21 | * UART sessions to a device 22 | * `adb shell` sessions, if the OEM `adbd` doesn't support TCP forwarding 23 | * telnet sessions 24 | * modem dial-ups without ppp 25 | * other kinds of console logins 26 | * mixed SSH/telnet/modem sessions 27 | * ... 28 | 29 | Just imagine you would have an invisible ppp session inside your shell session, 30 | without the remote peer actually supporting ppp. 31 | 32 | It runs on *Linux, Android, OSX, Windows, FreeBSD, NetBSD* and (possibly) *OpenBSD*. 33 | 34 | PSC also includes *SOCKS4* and *SOCKS5* proxy support in order to have actual 35 | web browsing sessions via port-shells or modem dial-ups remotely. 36 | 37 | Build 38 | ----- 39 | 40 | Edit the `Makefile` to reflect your pre shared keys, as defined at the top of the `Makefile`. 41 | 42 | Then just type `make` on *Linux* and *OSX*. 43 | 44 | On *BSD* you need to install *GNU make* and invoke `gmake` instead. 45 | 46 | On *Windows* you need to install [cygwin](https://cygwin.com/install.html) and select 47 | the appropriate `gcc, gcc-g++, make` and `git` packages. 48 | 49 | On *Linux*, PSC will use *Unix98* pseudo terminals, on other systems it will use *POSIX* 50 | pty's but that should be transparent to you. I once added *4.4BSD* pty and *SunOS* 51 | support back in the stone age for a particular reason, so it may or may not 52 | build even with *Solaris*. 53 | 54 | *proudly sponsored by:* 55 |

56 | 57 | 58 | 59 |

60 | 61 | 62 | Usage 63 | ----- 64 | 65 | Plain and simple. On your local box, execute `pscl`, and pass any 66 | TCP or UDP ports you want to forward *from* the remote site to a particular 67 | address. For example: 68 | 69 | ``` 70 | linux:~ > ./pscl -T 1234:[192.168.0.254]:22 -U 1234:[8.8.8.8]:53 71 | 72 | PortShellCrypter [pscl] v0.60 (C) 2006-2020 stealth -- github.com/stealth/psc 73 | 74 | pscl: set up local TCP port 1234 to proxy to 192.168.0.254:22 @ remote. 75 | pscl: set up local UDP port 1234 to proxy to 8.8.8.8:53 @ remote. 76 | 77 | pscl: Waiting for [pscr] session to appear ... 78 | linux:~ > 79 | 80 | [ UART / SSH / ... login to remote side ... ] 81 | ``` 82 | 83 | On the remote site (the last hop) with the shell session, no matter if its in 84 | a port-shell, SSH, console login etc, you execute `pscr`: 85 | 86 | 87 | ``` 88 | linux:~ > ./pscr 89 | 90 | PortShellCrypter [pscr] v0.60 (C) 2006-2020 stealth -- github.com/stealth/psc 91 | 92 | 93 | pscl: Seen STARTTLS sequence, enabling crypto. 94 | linux:~ > 95 | ``` 96 | 97 | Once you execute `pscr`, both ends establish a crypto handshake and lay an additional 98 | protocol over your existing session that is transparent for you. You can then 99 | connect to `127.0.0.1:1234` on your local box to reach `192.168.0.254:22` via 100 | TCP or the `8.8.8.8` resolver via UDP. This also works with [IPv6] addresses, 101 | if the remote site has IPv6 connectivity. Actually, you can even use it to translate 102 | IPv4 software to IPv6, since you always connect to `127.0.0.1` on the local side. 103 | 104 | You can pass multiple `-T` and `-U` parameters. If you lost track if your session 105 | is already e2e encrypted, you can send a `SIGUSR1` to the local `pscl` process, and it 106 | will tell you. 107 | 108 | PSC is also useful if you want to use tor from a remote SSH shell, where you 109 | can forward the socks5 and the DNS port to the remote hosts `127.0.0.1` address. 110 | Since SSH does not forward UDP packets, you would normally use two `socat` connectors 111 | or similar to resolve via the tor node. PSC has the advantage of keeping the UDP 112 | datagram boundaries, while `socat` over `SSH -L` may break datagram boundaries 113 | and create malformed DNS requests. 114 | 115 | The session will be encrypted with `aes_256_ctr` of a PSK that you choose in the 116 | `Makefile`. This crypto scheme is malleable, but adding AAD or OAD data blows up 117 | the packet size, where every byte counts since on interactive sessions and due to 118 | Base64 encoding, each typed character already causes much more data to be sent. 119 | 120 | UART sessions may be used via `screen` but for example not via `minicom` since 121 | minicom will create invisible windows with status lines and acts like a filter 122 | that destroys PSC's protocol. PSC tries to detect filtering and can live with 123 | certain amount of data mangling, but in some situations it is not possible to recover. 124 | Similar thing with `tmux`. You should avoid stacking pty handlers with PSC that 125 | mess/handle their incoming data too much. 126 | 127 | The `SHELL` environment variable needs to be set for both `pscl` and `pscr` in order 128 | for PSC to know which shell to execute on the pty. `SHELL` is set in most environments 129 | by default, but in case it isn't, PSC needs to be executed like `SHELL=/bin/bash pscl` 130 | etc. 131 | 132 | 133 | SOCKS4 and SOCKS5 support 134 | ------------------------- 135 | 136 | `pscl` also supports forwarding of TCP connections via *SOCKS4* (`-4 port`) and *SOCKS5* 137 | (`-5 port`). This sets up *port* as SOCKS port for TCP connections, so for instance you 138 | can browse remote networks from a port-shell session without the need to open any other 139 | connection during a pen-test. If you pass `-N` to `pscl`, it enables DNS name resolution 140 | on the remote side, so you can also use chrome with it. But be warned: There is a privacy 141 | problem with browsers that try to resolve a sequence of DNS names upon startup that 142 | is not under your control. Also, if your remote side has a broken DNS setup, your typing 143 | shell may block for several seconds if DNS reply packets are missing. There are no good 144 | async resolver functions which are embeddable and portable so I had to rely on 145 | `getaddrinfo()` in the single thread at the price of possible blockings for several seconds 146 | if DNS problems exist. Thats why name resolving has to be enabled explicitly. `pscr` 147 | tries to minimize this potential problem with DNS lookup caches though, so in most 148 | situation it should just work painlessly. 149 | If you pass `-X IP-address` (must be the first argument), you can bind your local proxy 150 | to an address different from `127.0.0.1`, so you can share the proxy in your local network. 151 | 152 | 153 | Bounce commands 154 | --------------- 155 | 156 | *psc* features allow TCP-connections or binary data blobs being forwarded from/to remote 157 | devices across multiple hops even if it is not possible to install the `pscr` binary at 158 | the remote site. This is very useful for forensic purposes if you do not have any means 159 | to otherwise download artefacts from the device (which can be an UART connected phone for example) 160 | or need to forward connections without touching the FS to not destroy evidence on the system 161 | or when the root-FS is ro mounted and you can't upload your tool-set. 162 | 163 | This is a really cool feature, as you can see your TCP connection hop through your local tty 164 | to a remote box without the need to install anything remotely. 165 | 166 | This solely works by local pty punkrock and handing over a bounce-command to `pscl` that it will 167 | drop on the remote shell (without `pscr` running) and some state engine magic that filters out 168 | and handles the data at the local side. Usually this requires to set the remote pty to raw mode 169 | at first before issuing the actual command and some other details that are passed to `-B`. The 170 | argument is split into the following parts: 171 | 172 | * The local port to trigger the command upon connect, followed by `:`, e.g. `1234:`. 173 | * The cmd that sets the remote tty to raw mode, usually `stty -echo raw` or 174 | `python -c "import tty;tty.setraw(0)"` (take care to get the quotes right, as `-B` also needs 175 | to be quoted) or anything similar. 176 | * A "GO" marker issued by remote that tells `pscl` to start sending data to avoid a race between 177 | `stty` actually happen and the start of the cmd, e.g. a `echo GO` is perfect. 178 | * The trigger command itself, e.g. `nc 127.0.0.1 22` to bounce local port 1234 to remote's SSH 179 | server 180 | * optionally a FIN marker issued by remote so you notice that trigger command has been finished 181 | i.e. you can kill your local connection to port 1234, which allows `pscl` to reset its tty state. 182 | `echo FIN` will do it. Recommended, as otherwise you can have trouble recognizing the end of 183 | your command. 184 | * All four previous commands are separated by `;` and enclosed in brackets. 185 | 186 | Examples: 187 | 188 | If you want to forward a TCP connection, this example requires `stty` and `nc` installed on the 189 | device, but it could theoretically be anything else that does equivalent. 190 | 191 | Start a local session: 192 | 193 | `./pscl -B '1234:[stty -echo raw;echo GO;nc example.com 22;echo FIN]'` 194 | 195 | This will issue the command `stty -echo raw;echo GO;nc example.com 22;echo FIN` to the remote 196 | device if you connect locally to port 1234 and then just forwards any data it sees back and forth 197 | and rate-limiting the traffic so it will not exceed the devices' tty speed (115200 is the default). 198 | 199 | When the pscl session is started, connect to the remote device by UART, `ssh -e none ...` or 200 | whatever it is and once you have the remote shell, also type locally: 201 | 202 | `ssh root@127.0.0.1 -p 1234` to bounce the SSH connection from your local box across the remote 203 | device to the `example.com` destination. Of course the `pscr` variant is preferred as `-B` can only 204 | bounce a single connection at a time (although you can pass multiple `-B` commands for various 205 | forwards) and theres a chance to hang the shell after the TCP session since the pty is in `raw -echo` 206 | mode and depending on whether the final remote peer also closes the connection, it might be 207 | that the shell just hangs after that. If you happen to find a pscl notification that the connection 208 | has finished and see a prompt, you should `reset` it, so that a new connection can be started. 209 | While data is being forwarded, you will see 7bit ASCII `<` and `>` notifications in `pscl` which 210 | are just local for easier debugging and progress detection. 211 | 212 | Note that the connection to the remote site has to be 8bit clean, i.e. the ssh, telnet, UART or 213 | whatever channel *must not handle escape sequences* (unlike when using `pscr`). For ssh connections 214 | this means you have to use `ssh -e none` in the `pscl` session. 215 | 216 | Next, following some examples to handle binary file xfer where *rfile* denotes the remote file and 217 | *lfile* the local file. 218 | 219 | To start a session to drop remote files, locally: 220 | 221 | `./pscl -B '1234:[stty -echo raw;echo GO;dd of=rfile.bin bs=1 count=7350;echo FIN]'` 222 | 223 | Where you need to specify the amount of data that the remote side is expecting. It would also 224 | work without (e.g. `cat>...`) but then the session will hang after transmission has finished as 225 | `cat` is endlessly expecting input. By using `dd count=...`, you will get a clean exit and be notified 226 | about it by the FIN marker. 227 | 228 | Then, ssh or whatever is necessary to get a shell on the remote device from within the just 229 | started `pscl` session. On a second terminal locally: 230 | 231 | `dd if=lfile.bin|nc 127.0.0.1 1234` 232 | 233 | which will connect to `pscl`'s local port 1234 and trigger the dump command on the remote side, 234 | forwarding the binary data of the local `lfile.bin` to remotes `rfile.bin`. Due to rate-limiting 235 | this can take a while and you *only trust your psc progress screen* whether the transfer is finished. 236 | The local `dd ...|nc ...` command will only show you the local status which can eat entire files 237 | in msecs due to local TCP buffers while the file is still being transfered through the pty. 238 | So make sure you only press `Ctrl-C` when the *pscl* screen tells you it is finished or you see 239 | the `FIN` end marker being echoed back to you on the `dd ...|nc ...` session. 240 | 241 | Likewise, similar commands could be used to transfer binary data from a remote device to the 242 | local box for forensic purposes. Again, start of the session locally: 243 | 244 | `./pscl -B '1234:[stty -echo raw;echo GO;dd if=rfile.bin]'` or 245 | 246 | `./pscl -B '1234:[stty -echo raw;echo GO;cat rfile.bin]'` 247 | 248 | Then, ssh to remote device to get the shell, then again locally: 249 | 250 | `nc 127.0.0.1 1234|dd of=lfile.bin bs=1 count=7350` 251 | 252 | To obtain `rfile.bin` of size 7350 copied to local file `lfile.bin` 253 | 254 | If `stty -echo raw` is not available on the device, something like 255 | `python -c "import tty;tty.setraw(0)"` also works. Note that on the remote device you need to have 256 | a tty (not just a port-shell) when using bounce commands, since the `stty` command to set raw mode 257 | requires a real tty. 258 | 259 | 260 | UART / modems / Flow Control 261 | ---------------------------- 262 | 263 | If *psc* runs across a serial connection, lost bits can kill all your fun. If you run 264 | without HW FC you will eventually experience bitloss and hung connections, in particular 265 | as there is no throttling when the device is sending data in your direction when using 266 | *bounce commands*. Dumping data to the device works better as this data goes through 267 | the `pscl` rate limits. 268 | 269 | However, here are some tips that worked for me under circumstances when it is not 270 | possible to use `pscr` on the device and HW FC. This only applies when using UARTs, as this 271 | is a potentially unreliable transport channel. 272 | 273 | * do not enable soft FC, as this would tamper the 8-bit channel 274 | * when possible use HW FC - or if not - you have to disable FC alltogether 275 | * Use `pscr` on the device so you can set rate limiting for data being sent into your direction. 276 | As the direction towards the device is always rate limited, you can use bounce commands to 277 | dump a cross-compiled `pscr` binary to the device and start a two-way rate limited session with it. 278 | * use high quality cables with proper shielding and UART chipsets with large buffers 279 | * apply the tio-limit patch from the contrib folder, as tio is buffering input bytes which could 280 | lead to writing-peeks that exceed the set rate 281 | * use `tio -o 1` or `-o 2` to add delays between sent output-bytes 282 | * use a conservative rate limitig (i.e. prefer `38400` although serial line has set `115200`) 283 | * compile `psc` with `-DRESPECT_UART_BUFSIZE=4096`, however this will make the session very slow 284 | 285 | Inside the `contrib` folder you will also find a `tio-noprefix` patch to disable escape-character 286 | processing but this patch is only necessary for older versions, as upstream already accepted and 287 | integrated this patch. I really recommend using `tio` when using UARTs. 288 | 289 | 290 | When using bounce commands across *tio*, you have to add to your `~/.tioconfig` file: 291 | 292 | ``` 293 | [default] 294 | 295 | prefix-ctrl-key = none 296 | ``` 297 | 298 | which disables ESC-handling and gives you an 8-bit clean channel. 299 | 300 | 301 | SIGUSR1 / SIGUSR2 302 | ----------------- 303 | 304 | You can send `SIGUSR1` to `pscl` so it tells you whether the session is encrypted. If the remote 305 | `pscr` dies or exits without possibility to signal that to the local part, `pscl` will stay 306 | in encryption mode and therefore hang. In this case you can force a reset to plaintext mode 307 | by sending `SIGUSR2`, so a new session can be started. 308 | 309 | Scripting 310 | --------- 311 | 312 | As of version 0.64, *psc* supports scripting-sockets so you no longer need `screen` to 313 | get/put files or dump paste buffers to the remote console. Instead, you start your local 314 | session like so: 315 | 316 | ``` 317 | ~ > ./pscl -S ~/psc.script_sock 318 | ``` 319 | 320 | You can then go ahead and use it as before. If you need to 'paste' something you do like: 321 | 322 | ``` 323 | ~ > ./pscsh -S ~/psc.script_sock -f script_/helloworld 324 | ``` 325 | 326 | This will 'type' the content of `script_/helloworld` to the console. While scripting, 327 | the stdin of `pscl` is blocked so that the injected input does not mix up with any 328 | typing. If `-S` is omitted in `pscsh`, `~/psc.script_sock` is used automatically. 329 | For safety reasons, scripts must start with the `script_` prefix. 330 | 331 | As a bonus, `pscr` now contains the ability to base64 en/decode files, even with CR 332 | embedded characters for convenience. It is compatible to `uuencode -m`. 333 | 334 | -------------------------------------------------------------------------------- /contrib/README.md: -------------------------------------------------------------------------------- 1 | contrib 2 | ======= 3 | 4 | This directory contains some contributions. 5 | 6 | tio noprefix patch 7 | ------------------ 8 | 9 | tio is a nice serial console client that has all the nice magic switches 10 | and cmdline friendlieness that you expect on Linux-like systems. 11 | 12 | Apply the patch to [tio](https://github.com/tio/tio) @commit `bfefd04b5567ba2b5` 13 | in order to allow serial console session w/o tapering of the I/O stream so you 14 | can pipe arbitrary data through your pty for the bounce command (this patch 15 | works like ssh's `-e none`). 16 | 17 | Adding this patch to current versions is not necessary anymore as its already 18 | been integrated. 19 | 20 | Then add to your `~/.tioconfig`: 21 | ``` 22 | prefix-ctrl-key = none 23 | ``` 24 | 25 | Which disables prefix parsing. 26 | 27 | 28 | 29 | tio limit patch 30 | --------------- 31 | 32 | tio will collect input bytes into a buffer before sending it to the serial line, 33 | which could lead to peeks above the acceptable baudrate. This dirty patch adds rate 34 | limiting in the write cycle. Also consider using `tio -o 1` if you get bit flips 35 | during transmission. 36 | 37 | -------------------------------------------------------------------------------- /contrib/tio-limit-ab678e6c882e19.patch: -------------------------------------------------------------------------------- 1 | diff --git a/src/tty.c b/src/tty.c 2 | index 74f6ab3..9392552 100644 3 | --- a/src/tty.c 4 | +++ b/src/tty.c 5 | @@ -27,6 +27,7 @@ 6 | #include 7 | #include 8 | #include 9 | +#include 10 | #include 11 | #include 12 | #include 13 | @@ -224,16 +225,27 @@ inline static unsigned char char_to_nibble(char c) 14 | void tty_sync(int fd) 15 | { 16 | ssize_t count; 17 | + struct timeval tv; 18 | + static struct timeval last_tv = {0, 0}; 19 | + uint32_t byte_rate = option.baudrate/10, idx = 0; 20 | 21 | while (tty_buffer_count > 0) 22 | { 23 | - count = write(fd, tty_buffer, tty_buffer_count); 24 | + gettimeofday(&tv, NULL); 25 | + uint64_t tdiff_usec = tv.tv_sec*1000000 + tv.tv_usec - (last_tv.tv_sec*1000000 + last_tv.tv_usec); 26 | + if (tdiff_usec < (1000000*1.0/byte_rate)) 27 | + continue; 28 | + last_tv.tv_sec = tv.tv_sec; 29 | + last_tv.tv_usec = tv.tv_usec; 30 | + 31 | + count = write(fd, tty_buffer + idx, 1); 32 | if (count < 0) 33 | { 34 | // Error 35 | tio_debug_printf("Write error while flushing tty buffer (%s)", strerror(errno)); 36 | break; 37 | } 38 | + idx += count; 39 | tty_buffer_count -= count; 40 | fsync(fd); 41 | tcdrain(fd); 42 | -------------------------------------------------------------------------------- /contrib/tio-noprefix-bfefd04b5567ba2b5.patch: -------------------------------------------------------------------------------- 1 | diff --git a/src/configfile.c b/src/configfile.c 2 | index 387063b..a96e0c3 100644 3 | --- a/src/configfile.c 4 | +++ b/src/configfile.c 5 | @@ -259,7 +259,10 @@ static int data_handler(void *user, const char *section, const char *name, 6 | } 7 | else if (!strcmp(name, "prefix-ctrl-key")) 8 | { 9 | - if (ctrl_key_code(value[0]) > 0) 10 | + if (!strcmp(value, "none")) { 11 | + option.prefix_off = 1; 12 | + } 13 | + else if (ctrl_key_code(value[0]) > 0) 14 | { 15 | option.prefix_code = ctrl_key_code(value[0]); 16 | option.prefix_key = value[0]; 17 | diff --git a/src/options.c b/src/options.c 18 | index 4f08e6d..1f88bbb 100644 19 | --- a/src/options.c 20 | +++ b/src/options.c 21 | @@ -86,6 +86,7 @@ struct option_t option = 22 | .hex_mode = false, 23 | .prefix_code = 20, // ctrl-t 24 | .prefix_key = 't', 25 | + .prefix_off = 0, 26 | .response_wait = false, 27 | .response_timeout = 100, 28 | .mute = false, 29 | diff --git a/src/options.h b/src/options.h 30 | index b18d8ff..64f8b96 100644 31 | --- a/src/options.h 32 | +++ b/src/options.h 33 | @@ -59,6 +59,7 @@ struct option_t 34 | bool hex_mode; 35 | unsigned char prefix_code; 36 | unsigned char prefix_key; 37 | + unsigned char prefix_off; 38 | bool response_wait; 39 | int response_timeout; 40 | bool mute; 41 | diff --git a/src/tty.c b/src/tty.c 42 | index cdab4b9..19cfda0 100644 43 | --- a/src/tty.c 44 | +++ b/src/tty.c 45 | @@ -338,7 +338,7 @@ void *tty_stdin_input_thread(void *arg) 46 | 47 | input_char = input_buffer[i]; 48 | 49 | - if (previous_char == option.prefix_code) 50 | + if (!option.prefix_off && previous_char == option.prefix_code) 51 | { 52 | if (input_char == option.prefix_code) 53 | { 54 | @@ -568,7 +568,7 @@ void handle_command_sequence(char input_char, char *output_char, bool *forward) 55 | } 56 | 57 | /* Handle escape key commands */ 58 | - if (previous_char == option.prefix_code) 59 | + if (!option.prefix_off && previous_char == option.prefix_code) 60 | { 61 | /* Do not forward input char to output by default */ 62 | *forward = false; 63 | @@ -1535,7 +1535,7 @@ int tty_connect(void) 64 | if (interactive_mode) 65 | { 66 | /* Do not forward prefix key */ 67 | - if (input_char == option.prefix_code) 68 | + if (!option.prefix_off && input_char == option.prefix_code) 69 | { 70 | forward = false; 71 | } 72 | -------------------------------------------------------------------------------- /src/Makefile: -------------------------------------------------------------------------------- 1 | # PSC Makefile 2 | 3 | # MUST use different keys for KEY1 and KEY2 4 | KEY1=\"secret1\" 5 | KEY2=\"secret2\" 6 | 7 | # 'p' encoded in hex so you can dump Makefile in pscl 8 | BANNER=\"\\x70sc-2022-STARTTLS-\" 9 | 10 | POSIX=-D_POSIX_C_SOURCE=200112L -D_XOPEN_SOURCE=600 11 | 12 | CXX=c++ 13 | DEFS=-DPSC_READ_KEY=$(KEY1) -DPSC_WRITE_KEY=$(KEY2) -DSTART_BANNER=$(BANNER) 14 | #DEFS+=-DRESPECT_UART_BUFSIZE=4096 15 | CXXFLAGS=-c -Wall -O2 -std=c++11 -pedantic 16 | 17 | .PHONY: all clean 18 | 19 | ifeq ($(shell uname), Linux) 20 | DEFS+=-DHAVE_UNIX98 21 | DEFS+=$(POSIX) 22 | else ifeq ($(shell uname), NetBSD) 23 | 24 | else ifeq ($(shell uname), FreeBSD) 25 | 26 | else ifeq ($(shell uname), OpenBSD) 27 | 28 | else ifeq ($(shell uname), Solaris) 29 | 30 | else ifeq ($(shell uname), Darwin) 31 | 32 | # Cygwin 33 | else 34 | DEFS+=$(POSIX) 35 | endif 36 | 37 | all: warn pscl pscr pscsh 38 | 39 | warn: 40 | @echo "Hint: Do not 'make' inside pscl session, as it echos PSC STARTTLS sequence." || true 41 | 42 | clean: 43 | rm -f *.o 44 | 45 | pscl: misc.o client.o pcwrap.o pty.o pty98.o net.o sha512.o aes.o 46 | $(CXX) $^ -o $@ 47 | 48 | pscr: misc.o server.o pty.o pty98.o pcwrap.o net.o sha512.o aes.o 49 | $(CXX) $^ -o $@ 50 | 51 | pscsh: pscsh.o 52 | $(CXX) $^ -o $@ 53 | 54 | pcwrap.o: pcwrap.cc 55 | $(CXX) $(DEFS) $(CXXFLAGS) $^ -o $@ 56 | 57 | client.o: client.cc 58 | $(CXX) $(DEFS) $(CXXFLAGS) $^ -o $@ 59 | 60 | server.o: server.cc 61 | $(CXX) $(DEFS) $(CXXFLAGS) $^ -o $@ 62 | 63 | misc.o: misc.cc 64 | $(CXX) $(DEFS) $(CXXFLAGS) $^ -o $@ 65 | 66 | pty.o: pty.cc 67 | $(CXX) $(DEFS) $(CXXFLAGS) $^ -o $@ 68 | 69 | pty98.o: pty98.cc 70 | $(CXX) $(DEFS) $(CXXFLAGS) $^ -o $@ 71 | 72 | net.o: net.cc 73 | $(CXX) $(DEFS) $(CXXFLAGS) $^ -o $@ 74 | 75 | aes.o: external/aes.cc 76 | $(CXX) $(DEFS) $(CXXFLAGS) $^ -o $@ 77 | 78 | sha512.o: external/sha512.cc 79 | $(CXX) $(DEFS) $(CXXFLAGS) $^ -o $@ 80 | 81 | pscsh.o: pscsh.cc 82 | $(CXX) $(DEFS) $(CXXFLAGS) $^ -o $@ 83 | 84 | -------------------------------------------------------------------------------- /src/Makefile.android.aarch64: -------------------------------------------------------------------------------- 1 | # 2 | # This is the Makefile for the Android flavor, Aarch64 3 | # 4 | 5 | 6 | # MUST use different keys for KEY1 and KEY2 7 | KEY1=\"secret1\" 8 | KEY2=\"secret2\" 9 | 10 | ROOT=/opt 11 | NDK=android-ndk-r17b 12 | 13 | PREFIX=$(ROOT)/$(NDK)/toolchains/aarch64-linux-android-4.9/prebuilt/linux-x86_64/bin/aarch64-linux-android- 14 | SYSROOT=--sysroot=$(ROOT)/$(NDK)/platforms/android-24/arch-arm64/ 15 | SYSROOT+=-isysroot $(ROOT)/$(NDK)/sysroot 16 | 17 | INC=-isystem $(ROOT)/$(NDK)/sysroot/usr/include/aarch64-linux-android\ 18 | -I$(ROOT)/$(NDK)/sources/cxx-stl/gnu-libstdc++/include\ 19 | -I$(ROOT)/$(NDK)/sources/cxx-stl/gnu-libstdc++/4.9/include/\ 20 | -I$(ROOT)/$(NDK)/sources/cxx-stl/gnu-libstdc++/4.9/libs/arm64-v8a/include 21 | 22 | LIB=-Wl,$(ROOT)/$(NDK)/sources/cxx-stl/gnu-libstdc++/4.9/libs/arm64-v8a/libgnustl_static.a 23 | 24 | DEFS=-DPSC_READ_KEY=$(KEY1) -DPSC_WRITE_KEY=$(KEY2) 25 | DEFS+=-DHAVE_UNIX98 26 | 27 | CXX=$(PREFIX)gcc $(SYSROOT) $(INC) 28 | LD=$(PREFIX)gcc -pie $(SYSROOT) 29 | 30 | CXXFLAGS=-c -O2 -Wall -pedantic -std=c++11 -fPIC 31 | 32 | 33 | .PHONY: all clean 34 | 35 | 36 | all: pscl pscr pscsh 37 | 38 | clean: 39 | rm -f *.o 40 | 41 | pscl: misc.o client.o pcwrap.o pty.o pty98.o net.o sha512.o aes.o 42 | $(LD) $^ -o $@ $(LIB) 43 | 44 | pscr: misc.o server.o pty.o pty98.o pcwrap.o net.o sha512.o aes.o 45 | $(LD) $^ -o $@ $(LIB) 46 | 47 | pscsh: pscsh.o 48 | $(LD) $^ -o $@ $(LIB) 49 | 50 | pcwrap.o: pcwrap.cc 51 | $(CXX) $(DEFS) $(CXXFLAGS) $^ -o $@ 52 | 53 | client.o: client.cc 54 | $(CXX) $(DEFS) $(CXXFLAGS) $^ -o $@ 55 | 56 | server.o: server.cc 57 | $(CXX) $(DEFS) $(CXXFLAGS) $^ -o $@ 58 | 59 | misc.o: misc.cc 60 | $(CXX) $(DEFS) $(CXXFLAGS) $^ -o $@ 61 | 62 | pty.o: pty.cc 63 | $(CXX) $(DEFS) $(CXXFLAGS) $^ -o $@ 64 | 65 | pty98.o: pty98.cc 66 | $(CXX) $(DEFS) $(CXXFLAGS) $^ -o $@ 67 | 68 | net.o: net.cc 69 | $(CXX) $(DEFS) $(CXXFLAGS) $^ -o $@ 70 | 71 | aes.o: external/aes.cc 72 | $(CXX) $(DEFS) $(CXXFLAGS) $^ -o $@ 73 | 74 | sha512.o: external/sha512.cc 75 | $(CXX) $(DEFS) $(CXXFLAGS) $^ -o $@ 76 | 77 | pscsh.o: pscsh.cc 78 | $(CXX) $(DEFS) $(CXXFLAGS) $^ -o $@ 79 | 80 | -------------------------------------------------------------------------------- /src/client.cc: -------------------------------------------------------------------------------- 1 | /* 2 | * This file is part of port shell crypter (psc). 3 | * 4 | * (C) 2006-2024 by Sebastian Krahmer, 5 | * sebastian [dot] krahmer [at] gmail [dot] com 6 | * 7 | * psc is free software: you can redistribute it and/or modify 8 | * it under the terms of the GNU General Public License as published by 9 | * the Free Software Foundation, either version 3 of the License, or 10 | * (at your option) any later version. 11 | * 12 | * psc is distributed in the hope that it will be useful, 13 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 14 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 | * GNU General Public License for more details. 16 | * 17 | * You should have received a copy of the GNU General Public License 18 | * along with psc. If not, see . 19 | */ 20 | 21 | 22 | #include 23 | #include 24 | #include 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 | 40 | #include "net.h" 41 | #include "pty.h" 42 | #include "pcwrap.h" 43 | #include "misc.h" 44 | 45 | 46 | using namespace std; 47 | using namespace ns_psc; 48 | 49 | namespace ns_psc { 50 | 51 | pc_wrap *psc = nullptr; 52 | 53 | struct termios global_tcattr, exit_tattr; 54 | 55 | } 56 | 57 | void sig_chld(int) 58 | { 59 | tcsetattr(0, TCSANOW, &exit_tattr); 60 | printf("pscl: exiting\n"); 61 | _exit(0); 62 | } 63 | 64 | 65 | void sig_usr1(int) 66 | { 67 | if (!psc) 68 | return; 69 | 70 | if (psc->is_crypted()) 71 | printf("\r\npscl: encryption enabled\r\n"); 72 | else 73 | printf("\r\npscl: encryption disabled\r\n"); 74 | } 75 | 76 | 77 | void sig_usr2(int) 78 | { 79 | if (!psc) 80 | return; 81 | 82 | printf("\r\npscl: Disabling encryption on USR2\r\n"); 83 | psc->reset(); 84 | } 85 | 86 | 87 | bool winsize_changed = 0; 88 | 89 | void sig_win(int) 90 | { 91 | winsize_changed = 1; 92 | } 93 | 94 | 95 | void usage(const char *argv0) 96 | { 97 | printf("Usage: %s\t[-4 socks4 lport] [-5 socks5 lport] [-T lport:[ip]:rport]\n" 98 | "\t\t[-U lport:[ip]:rport] [-X local proxy IP (127.0.0.1 dflt)] [-N]\n" 99 | "\t\t[-B lport:[bounce cmd] [-S script socket] [-l baud limit]\n\n", argv0); 100 | } 101 | 102 | 103 | // do not use replace_if() for easier C++98 ports 104 | string trim_bcmd_output(const string &s) 105 | { 106 | string ret = s.substr(0, 75); 107 | size_t retl = ret.size(); 108 | for (size_t i = 0; i < retl; ++i) { 109 | if (ret[i] < 32 || ret[i] > 125) 110 | ret[i] = '.'; 111 | } 112 | return ret; 113 | } 114 | 115 | 116 | int proxy_loop() 117 | { 118 | 119 | #ifdef HAVE_UNIX98 120 | pty98 pt; 121 | #else 122 | pty pt; 123 | #endif 124 | pid_t pid; 125 | int r, afd = -1, i, bcmd_accept_fd = -1; 126 | 127 | char sbuf[BLOCK_SIZE/2] = {0}; // 1 MTU 128 | struct termios tattr; 129 | 130 | if (pt.open() < 0) 131 | die(pt.why()); 132 | fix_size(pt.slave()); 133 | 134 | if (tcgetattr(0, &tattr) < 0) 135 | die("pscl: tcgetattr"); 136 | 137 | exit_tattr = tattr; 138 | 139 | cfmakeraw(&tattr); 140 | tattr.c_cc[VMIN] = 1; 141 | tattr.c_cc[VTIME] = 0; 142 | tattr.c_lflag &= ~ISIG; 143 | 144 | //tattr.c_lflag &= ~ECHO; 145 | 146 | global_tcattr = tattr; 147 | if (tcsetattr(0, TCSANOW, &tattr) < 0) 148 | die("pscl: tcsetattr"); 149 | 150 | struct rlimit rl; 151 | if (getrlimit(RLIMIT_NOFILE, &rl) < 0) 152 | die("getrlimit"); 153 | if (rl.rlim_cur > FDID_MAX) { 154 | rl.rlim_cur = rl.rlim_max = FDID_MAX; 155 | setrlimit(RLIMIT_NOFILE, &rl); 156 | } 157 | 158 | if ((pid = fork()) == 0) { 159 | char *a[] = {getenv("SHELL"), nullptr}; 160 | extern char **environ; 161 | 162 | if (!*a) { 163 | die("pscl: no shell set via $SHELL"); 164 | } 165 | 166 | close(0); close(1); close(2); 167 | dup2(pt.slave(), 0); dup2(pt.slave(), 1); 168 | dup2(pt.slave(), 2); 169 | setsid(); 170 | ioctl(0, TIOCSCTTY, 0); 171 | pt.close(); 172 | for (unsigned int i = 3; i < rl.rlim_cur; ++i) 173 | close(i); 174 | execve(*a, a, environ); 175 | die("pscl: execve"); 176 | } else if (pid < 0) 177 | die("pscl: fork"); 178 | 179 | psc = new (nothrow) pc_wrap(pt.master(), pt.master()); 180 | if (!psc) 181 | die("new pc_wrap OOM"); 182 | 183 | if (psc->init(PSC_WRITE_KEY, PSC_READ_KEY, 0) < 0) 184 | die(psc->why()); 185 | close(pt.slave()); 186 | 187 | struct pollfd *pfds = new (nothrow) pollfd[rl.rlim_cur]; 188 | struct state *fd2state = new (nothrow) state[rl.rlim_cur]; 189 | 190 | fd2state[0].fd = 0; 191 | fd2state[0].state = STATE_STDIN; 192 | 193 | fd2state[1].fd = 1; 194 | fd2state[1].state = STATE_STDOUT; 195 | 196 | fd2state[pt.master()].fd = pt.master(); 197 | fd2state[pt.master()].state = STATE_PTY; 198 | 199 | for (unsigned int i = 0; i < rl.rlim_cur; ++i) { 200 | pfds[i].fd = -1; 201 | pfds[i].events = pfds[i].revents = 0; 202 | } 203 | 204 | pfds[0].fd = 0; 205 | pfds[0].events = POLLIN; 206 | pfds[1].fd = 1; 207 | pfds[pt.master()].fd = pt.master(); 208 | pfds[pt.master()].events = POLLIN; 209 | 210 | for (auto it = config::tcp_listens.begin(); it != config::tcp_listens.end(); ++it) { 211 | if ((r = tcp_listen(config::local_proxy_ip, it->first)) < 0) 212 | continue; 213 | pfds[r].fd = r; 214 | pfds[r].events = POLLIN; 215 | 216 | fd2state[r].fd = r; 217 | fd2state[r].rnode = it->second; 218 | fd2state[r].state = STATE_ACCEPT; 219 | } 220 | 221 | for (auto it = config::udp_listens.begin(); it != config::udp_listens.end(); ++it) { 222 | if ((r = udp_listen(config::local_proxy_ip, it->first)) < 0) 223 | continue; 224 | pfds[r].fd = r; 225 | pfds[r].events = POLLIN; 226 | 227 | fd2state[r].fd = r; 228 | fd2state[r].rnode = it->second; 229 | fd2state[r].state = STATE_UDPSERVER; 230 | } 231 | 232 | // bouncing via bounce command 233 | for (auto it = config::bcmd_tcp_listens.begin(); it != config::bcmd_tcp_listens.end(); ++it) { 234 | if ((r = tcp_listen(config::local_proxy_ip, it->first)) < 0) 235 | continue; 236 | pfds[r].fd = r; 237 | pfds[r].events = POLLIN; 238 | 239 | fd2state[r].fd = r; 240 | fd2state[r].rnode = it->second; // actually the cmd e.g. `stty -echo raw;nc example.com 22` 241 | fd2state[r].state = STATE_BCMD_ACCEPT; 242 | } 243 | 244 | if (config::socks5_fd != -1) { 245 | pfds[config::socks5_fd].fd = config::socks5_fd; 246 | pfds[config::socks5_fd].events = POLLIN; 247 | 248 | fd2state[config::socks5_fd].fd = config::socks5_fd; 249 | fd2state[config::socks5_fd].rnode = ""; 250 | fd2state[config::socks5_fd].state = STATE_SOCKS5_ACCEPT; 251 | } 252 | 253 | if (config::socks4_fd != -1) { 254 | pfds[config::socks4_fd].fd = config::socks4_fd; 255 | pfds[config::socks4_fd].events = POLLIN; 256 | 257 | fd2state[config::socks4_fd].fd = config::socks4_fd; 258 | fd2state[config::socks4_fd].rnode = ""; 259 | fd2state[config::socks4_fd].state = STATE_SOCKS4_ACCEPT; 260 | } 261 | 262 | if (config::script_sock != -1) { 263 | pfds[config::script_sock].fd = config::script_sock; 264 | pfds[config::script_sock].events = POLLIN; 265 | 266 | fd2state[config::script_sock].fd = config::script_sock; 267 | fd2state[config::script_sock].rnode = ""; 268 | fd2state[config::script_sock].state = STATE_SCRIPT_ACCEPT; 269 | } 270 | 271 | // Since we have no fd per "connection" for UDP, we need to keep track of 272 | // local sockaddrs <-> id by ourself in order to know to which dst to send replies 273 | // we receive from remote 274 | udp_node2id udp_nodes2id; 275 | 276 | int max_fd = rl.rlim_cur - 1, script_fd = -1; 277 | #ifdef RESPECT_UART_BUFSIZE 278 | uint32_t tx_rate_cnt = 0; 279 | #endif 280 | 281 | string ext_cmd = "", tbuf = ""; 282 | string bcmd = "", bcmd_in_log_str = "", bcmd_out_log_str = "", bcmd_buffered_output = ""; 283 | const string BCMD_GO_CMD = "GO\n"; 284 | 285 | enum { CHUNK_SIZE = 8192 }; 286 | 287 | timeval last_tv; 288 | memset(&last_tv, 0, sizeof(last_tv)); 289 | 290 | for (;;) { 291 | 292 | memset(sbuf, 0, sizeof(sbuf)); 293 | 294 | if (winsize_changed && psc->is_crypted()) { 295 | fd2state[pt.master()].obuf += psc->wsize_cmd(); 296 | pfds[pt.master()].events |= POLLOUT; 297 | winsize_changed = 0; 298 | } 299 | 300 | for (i = rl.rlim_cur - 1; i > 0; --i) { 301 | if (fd2state[i].state != STATE_INVALID && fd2state[i].fd != -1) { 302 | max_fd = i; 303 | break; 304 | } 305 | } 306 | 307 | if ((r = poll(pfds, max_fd + 1, 1000)) < 0) { 308 | if (errno == EINTR) 309 | continue; 310 | else 311 | die("pscl: poll"); 312 | } 313 | 314 | time_t now = time(nullptr); 315 | 316 | for (i = 0; i <= max_fd; ++i) { 317 | 318 | if (fd2state[i].state == STATE_INVALID) 319 | continue; 320 | 321 | if ((fd2state[i].state == STATE_CLOSING && (now - fd2state[i].time) > CLOSING_TIME) || 322 | (fd2state[i].state == STATE_CONNECT && (now - fd2state[i].time) > CONNECT_TIME)) { 323 | 324 | if (fd2state[i].state == STATE_CONNECT) { 325 | pfds[pt.master()].events |= POLLOUT; 326 | fd2state[pt.master()].obuf += psc->possibly_b64encrypt("C:T:F:", fd2state[i].rnode); // signal interrupted connection to remote 327 | tcp_nodes2sock.erase(fd2state[i].rnode); 328 | } 329 | 330 | close(i); 331 | fd2state[i].fd = -1; 332 | fd2state[i].state = STATE_INVALID; 333 | pfds[i].fd = -1; 334 | pfds[i].events = 0; 335 | continue; 336 | } 337 | 338 | if (pfds[i].revents & (POLLERR|POLLHUP|POLLNVAL)) { 339 | if (fd2state[i].state == STATE_STDIN || fd2state[i].state == STATE_PTY) 340 | die("pscl: TTY hangup"); 341 | if (fd2state[i].state == STATE_CONNECTED || fd2state[i].state == STATE_CONNECT) { 342 | pfds[pt.master()].events |= POLLOUT; 343 | fd2state[pt.master()].obuf += psc->possibly_b64encrypt("C:T:F:", fd2state[i].rnode); // signal finished connection to remote 344 | tcp_nodes2sock.erase(fd2state[i].rnode); 345 | } 346 | 347 | if (fd2state[i].state == STATE_SCRIPT_IO || 348 | fd2state[i].state == STATE_BCMD_CONNECT || fd2state[i].state == STATE_BCMD_CONNECTED) { 349 | pfds[0].events |= POLLIN; // reactivate stdin 350 | if (config::script_sock >= 0) 351 | pfds[config::script_sock].events |= POLLIN; 352 | if (i == bcmd_accept_fd) 353 | bcmd_accept_fd = -1; 354 | } 355 | 356 | close(i); 357 | fd2state[i].fd = -1; 358 | fd2state[i].state = STATE_INVALID; 359 | fd2state[i].obuf.clear(); 360 | pfds[i].fd = -1; 361 | pfds[i].events = 0; 362 | continue; 363 | } 364 | 365 | errno = 0; 366 | ext_cmd.clear(); 367 | 368 | if (pfds[i].revents & POLLIN) { 369 | pfds[i].revents = 0; 370 | if (fd2state[i].state == STATE_STDIN) { 371 | 372 | if ((r = read(0, sbuf, sizeof(sbuf))) <= 0) { 373 | if (errno == EINTR) 374 | continue; 375 | else 376 | die("pscl: read"); 377 | } 378 | fd2state[pt.master()].obuf += psc->possibly_b64encrypt("D:0:", string(sbuf, r)); 379 | pfds[pt.master()].events |= POLLOUT; 380 | 381 | } else if (fd2state[i].state == STATE_PTY) { 382 | int starttls = 0; 383 | bool nosys = 0; 384 | do { 385 | tbuf = ext_cmd = ""; 386 | if ((r = psc->read(nosys, tbuf, ext_cmd, starttls)) < 0) 387 | die(psc->why()); 388 | 389 | nosys = 1; 390 | 391 | // STARTTLS/end-sequence seen 392 | if (starttls == 1) { 393 | winsize_changed = 1; 394 | continue; 395 | } 396 | 397 | // ext_cmd can have been filled by psc->read() only when already STARTTLS happened 398 | if (ext_cmd.size() > 0) { 399 | cmd_handler(ext_cmd, fd2state, pfds); 400 | } else if (tbuf.size() > 0) { 401 | 402 | // Are we running in a bounce command session? 403 | if (bcmd_accept_fd >= 0) { 404 | 405 | // do not echo back bcmd inject that happens just after STATE_BCMD_CONNECT 406 | if (fd2state[bcmd_accept_fd].state == STATE_BCMD_CONNECT) { 407 | bcmd_buffered_output += tbuf; 408 | 409 | // If we see the desired string "GO" echoed back, strip off until newline and only forward real potential data that follows "GO\n". 410 | // Otherwise the echo was just partial and we need to wait until full echo 411 | string::size_type nl = string::npos; 412 | if ((nl = bcmd_buffered_output.find(BCMD_GO_CMD)) != string::npos) { 413 | fd2state[bcmd_accept_fd].obuf += bcmd_buffered_output.substr(nl + BCMD_GO_CMD.size()); 414 | fd2state[bcmd_accept_fd].state = STATE_BCMD_CONNECTED; 415 | pfds[bcmd_accept_fd].events |= POLLIN; 416 | if (!fd2state[bcmd_accept_fd].obuf.empty()) 417 | pfds[bcmd_accept_fd].events |= POLLOUT; 418 | bcmd_in_log_str += bcmd_buffered_output; 419 | bcmd_buffered_output.clear(); 420 | 421 | sleep(3); // We need to sleep since between "echo GO" and "nc 127.0.0.1 22" commands on remote, we want nc to have the 422 | // stdin/stdout assigned and not dumping data to it before nc started 423 | } 424 | } else { // must be STATE_BCMD_CONNECTED, forward as is 425 | fd2state[bcmd_accept_fd].obuf += tbuf; 426 | pfds[bcmd_accept_fd].events |= POLLOUT; 427 | 428 | bcmd_in_log_str += tbuf; 429 | 430 | if (bcmd_in_log_str.size() >= 75) { 431 | fd2state[1].obuf += "\r\n< " + trim_bcmd_output(bcmd_in_log_str); 432 | bcmd_in_log_str.clear(); 433 | pfds[1].events |= POLLOUT; 434 | } 435 | } 436 | 437 | // No -> only forward to stdout as is 438 | } else { 439 | fd2state[1].obuf += tbuf; 440 | pfds[1].events |= POLLOUT; 441 | } 442 | 443 | // mirror to script sock if opened 444 | if (script_fd >= 0) { 445 | fd2state[script_fd].time = now; 446 | fd2state[script_fd].obuf += tbuf; 447 | pfds[script_fd].events |= POLLOUT; 448 | } 449 | } 450 | } while (r == 1); 451 | 452 | } else if (fd2state[i].state == STATE_ACCEPT) { 453 | if ((afd = accept(i, nullptr, nullptr)) < 0) 454 | continue; 455 | 456 | maybe_set_rcvbuf(afd, config::rate_limit_bytes); 457 | 458 | // append ID part of host/port/id/ header. We use the accepted sock fd 459 | // as ID, as this is unique and identifies the TCP connection 460 | char id[16] = {0}; 461 | snprintf(id, sizeof(id) - 1, "%04hx/", (uint16_t)afd); 462 | 463 | pfds[afd].fd = afd; 464 | pfds[afd].events = 0; // dont accept data until remote peer established proxy conn 465 | 466 | fd2state[afd].fd = afd; 467 | fd2state[afd].rnode = fd2state[i].rnode + id; 468 | fd2state[afd].state = STATE_CONNECT; 469 | fd2state[afd].time = now; 470 | fd2state[afd].obuf.clear(); 471 | 472 | tcp_nodes2sock[fd2state[afd].rnode] = afd; 473 | 474 | pfds[pt.master()].events |= POLLOUT; 475 | fd2state[pt.master()].obuf += psc->possibly_b64encrypt("C:T:N:", fd2state[afd].rnode); // trigger tcp_connect() on remote side 476 | 477 | } else if (fd2state[i].state == STATE_BCMD_ACCEPT) { 478 | if ((afd = accept(i, nullptr, nullptr)) < 0) 479 | continue; 480 | 481 | // can only handle one bcmd bounce at once 482 | if (bcmd_accept_fd >= 0) { 483 | close(afd); 484 | continue; 485 | } 486 | 487 | maybe_set_rcvbuf(afd, config::rate_limit_bytes); 488 | 489 | pfds[afd].fd = afd; 490 | pfds[afd].events = 0; // dont accept data until bcmd has chance to connect 491 | 492 | fd2state[afd].fd = afd; 493 | fd2state[afd].rnode = fd2state[i].rnode; 494 | fd2state[afd].state = STATE_BCMD_CONNECT; // will change to STATE_BCMD_CONNECTED when reading echo back from PTY 495 | fd2state[afd].time = now; 496 | fd2state[afd].obuf.clear(); 497 | 498 | pfds[pt.master()].events |= POLLOUT; 499 | fd2state[pt.master()].obuf += fd2state[i].rnode + "\n"; // trigger connect via command (e.g. `...nc example.com 22`) on remote side 500 | 501 | bcmd_accept_fd = afd; 502 | bcmd = fd2state[i].rnode; 503 | 504 | fd2state[1].obuf += "\r\n> " + bcmd; 505 | pfds[1].events |= POLLOUT; 506 | pfds[0].events = 0; // block stdin typing while bouncing 507 | 508 | } else if (fd2state[i].state == STATE_SOCKS5_ACCEPT) { 509 | if ((afd = accept(i, nullptr, nullptr)) < 0) 510 | continue; 511 | 512 | maybe_set_rcvbuf(afd, config::rate_limit_bytes); 513 | 514 | pfds[afd].fd = afd; 515 | pfds[afd].events = POLLIN; // wait for SOCKS5 proto requests 516 | fd2state[afd].fd = afd; 517 | fd2state[afd].rnode = ""; // filled in by later state 518 | fd2state[afd].state = STATE_SOCKS5_AUTH1; 519 | fd2state[afd].time = now; 520 | fd2state[afd].obuf.clear(); 521 | 522 | } else if (fd2state[i].state == STATE_SOCKS4_ACCEPT) { 523 | if ((afd = accept(i, nullptr, nullptr)) < 0) 524 | continue; 525 | 526 | maybe_set_rcvbuf(afd, config::rate_limit_bytes); 527 | 528 | pfds[afd].fd = afd; 529 | pfds[afd].events = POLLIN; // wait for SOCKS4 proto requests 530 | fd2state[afd].fd = afd; 531 | fd2state[afd].rnode = ""; // filled in by later state 532 | fd2state[afd].state = STATE_SOCKS4_AUTH; 533 | fd2state[afd].time = now; 534 | fd2state[afd].obuf.clear(); 535 | 536 | } else if (fd2state[i].state == STATE_CONNECTED) { 537 | if (config::rate_limit_bytes && fd2state[pt.master()].obuf.size() > MAX_RX_ON_LIMITS) 538 | continue; 539 | if ((r = recv(i, sbuf, sizeof(sbuf), 0)) <= 0) { 540 | close(i); 541 | pfds[i].fd = -1; 542 | pfds[i].events = 0; 543 | fd2state[i].state = STATE_INVALID; 544 | fd2state[i].fd = -1; 545 | fd2state[i].obuf.clear(); 546 | tcp_nodes2sock.erase(fd2state[i].rnode); 547 | 548 | pfds[pt.master()].events |= POLLOUT; 549 | fd2state[pt.master()].obuf += psc->possibly_b64encrypt("C:T:F:", fd2state[i].rnode); // signal finished connection via PTY to remote 550 | continue; 551 | } 552 | pfds[pt.master()].events |= POLLOUT; 553 | fd2state[pt.master()].obuf += psc->possibly_b64encrypt("C:T:S:", fd2state[i].rnode + string(sbuf, r)); 554 | fd2state[i].time = now; 555 | 556 | } else if (fd2state[i].state == STATE_BCMD_CONNECTED) { 557 | if (config::rate_limit_bytes && fd2state[pt.master()].obuf.size() > MAX_RX_ON_LIMITS) 558 | continue; 559 | if ((r = recv(i, sbuf, sizeof(sbuf), 0)) <= 0) { 560 | close(i); 561 | pfds[i].fd = -1; 562 | pfds[i].events = 0; 563 | fd2state[i].state = STATE_INVALID; 564 | fd2state[i].fd = -1; 565 | fd2state[i].obuf.clear(); 566 | 567 | pfds[0].events |= POLLIN; // reactivate stdin 568 | pfds[1].events |= POLLOUT; 569 | fd2state[1].obuf += "\r\n> Bounce cmd finished, type `reset`.\r\n"; 570 | bcmd = ""; 571 | bcmd_accept_fd = -1; 572 | continue; 573 | } 574 | pfds[pt.master()].events |= POLLOUT; 575 | fd2state[pt.master()].obuf += string(sbuf, r); 576 | fd2state[i].time = now; 577 | 578 | } else if (fd2state[i].state == STATE_SOCKS4_AUTH) { 579 | 580 | socks4_req *s4r = reinterpret_cast(sbuf); 581 | 582 | // expect SOCKS4 request and send positive response 583 | memset(sbuf, 0, sizeof(sbuf)); 584 | if ((r = recv(i, sbuf, sizeof(sbuf), 0)) <= 0 || sbuf[0] != 4) { 585 | close(i); 586 | pfds[i].fd = -1; 587 | pfds[i].events = 0; 588 | fd2state[i].state = STATE_INVALID; 589 | fd2state[i].fd = -1; 590 | fd2state[i].obuf.clear(); 591 | continue; 592 | } 593 | 594 | s4r->ver = 0; 595 | s4r->cmd = 0x5a; // request granted 596 | fd2state[i].obuf += string(sbuf, 8); // orig req w/o ID 597 | 598 | char dst[128] = {0}; 599 | uint16_t rport = 0; 600 | 601 | inet_ntop(AF_INET, &s4r->dst, dst, sizeof(dst) - 1); 602 | rport = ntohs(s4r->dport); 603 | 604 | // Now that we know where connection is going to, we can build 605 | // IP/port/ID header 606 | char hdr[256] = {0}; 607 | snprintf(hdr, sizeof(hdr) - 1, "%s/%04hx/%04hx/", dst, rport, (uint16_t)i); 608 | 609 | fd2state[i].rnode = hdr; 610 | fd2state[i].state = STATE_CONNECT; 611 | fd2state[i].time = now; 612 | 613 | tcp_nodes2sock[fd2state[i].rnode] = i; 614 | 615 | pfds[pt.master()].events |= POLLOUT; 616 | fd2state[pt.master()].obuf += psc->possibly_b64encrypt("C:T:N:", fd2state[i].rnode); // trigger tcp_connect() on remote side 617 | 618 | pfds[i].events = POLLOUT; // don't take data until remote site established connection, so *only* POLLOUT 619 | 620 | } else if (fd2state[i].state == STATE_SOCKS5_AUTH1) { 621 | 622 | // expect SOCKS5 auth request (none) and send positive response 623 | memset(sbuf, 0, sizeof(sbuf)); 624 | if ((r = recv(i, sbuf, sizeof(sbuf), 0)) <= 0 || sbuf[0] != 5) { 625 | close(i); 626 | pfds[i].fd = -1; 627 | pfds[i].events = 0; 628 | fd2state[i].state = STATE_INVALID; 629 | fd2state[i].fd = -1; 630 | fd2state[i].obuf.clear(); 631 | continue; 632 | } 633 | pfds[i].events |= POLLOUT; 634 | fd2state[i].state = STATE_SOCKS5_AUTH2; 635 | fd2state[i].obuf += string("\x05\x00", 2); 636 | fd2state[i].time = now; 637 | } else if (fd2state[i].state == STATE_SOCKS5_AUTH2) { 638 | 639 | memset(sbuf, 0, sizeof(sbuf)); 640 | socks5_req *s5r = reinterpret_cast(sbuf); 641 | 642 | // expect SOCKS5 connect request 643 | if ((r = recv(i, sbuf, sizeof(sbuf), 0)) < 10 || 644 | s5r->vers != 5 || // wrong version? 645 | (s5r->atype != 1 && s5r->atype != 3 && s5r->atype != 4) || // not or DNS name or IPv4 or IPv6? 646 | s5r->cmd != 1 || // not a TCP-connect? 647 | (s5r->atype == 3 && s5r->name.nlen > MAX_NAME_LEN) || // DNS name too long? 648 | (s5r->atype == 3 && !config::socks5_dns)) { // SOCKS5 resolving not enabled? 649 | s5r->cmd = 0x08; // atype not supported 650 | writen(i, sbuf, 2); 651 | close(i); 652 | pfds[i].fd = -1; 653 | pfds[i].events = 0; 654 | fd2state[i].state = STATE_INVALID; 655 | fd2state[i].fd = -1; 656 | fd2state[i].obuf.clear(); 657 | continue; 658 | } 659 | 660 | char dst[128] = {0}; 661 | uint16_t rport = 0; 662 | 663 | // IPv4 664 | if (s5r->atype == 1) { 665 | inet_ntop(AF_INET, &s5r->v4.dst, dst, sizeof(dst) - 1); 666 | rport = ntohs(s5r->v4.dport); 667 | 668 | // IPv6 669 | } else if (s5r->atype == 4) { 670 | inet_ntop(AF_INET6, &s5r->v6.dst, dst, sizeof(dst) - 1); 671 | rport = ntohs(s5r->v6.dport); 672 | 673 | // DNS name 674 | } else { 675 | memcpy(dst, s5r->name.name, s5r->name.nlen); 676 | uint16_t tmp; 677 | memcpy(&tmp, s5r->name.name + s5r->name.nlen, sizeof(tmp)); 678 | rport = ntohs(tmp); 679 | } 680 | 681 | // Now that we know where connection is going to, we can build 682 | // IP/port/ID header 683 | char hdr[256] = {0}; 684 | snprintf(hdr, sizeof(hdr) - 1, "%s/%04hx/%04hx/", dst, rport, (uint16_t)i); 685 | 686 | fd2state[i].rnode = hdr; 687 | fd2state[i].state = STATE_CONNECT; 688 | fd2state[i].time = now; 689 | 690 | tcp_nodes2sock[fd2state[i].rnode] = i; 691 | 692 | pfds[pt.master()].events |= POLLOUT; 693 | fd2state[pt.master()].obuf += psc->possibly_b64encrypt("C:T:N:", fd2state[i].rnode); // trigger tcp_connect() on remote side 694 | 695 | s5r->cmd = 0; // response status to socks5 client 696 | fd2state[i].obuf += string(sbuf, r); 697 | 698 | pfds[i].events = POLLOUT; // don't take data until remote site established connection, so *only* POLLOUT 699 | 700 | } else if (fd2state[i].state == STATE_UDPSERVER) { 701 | 702 | char sin[sizeof(sockaddr_in) + sizeof(sockaddr_in6)] = {0}; 703 | socklen_t sinlen = sizeof(sin); 704 | if ((r = recvfrom(i, sbuf, sizeof(sbuf), 0, reinterpret_cast(&sin), &sinlen)) <= 0) 705 | continue; 706 | 707 | // in UDP case, we need to generate a unique ID based on dgram origin. If the origin was already 708 | // given an unique ID, put() will find and return it transparently 709 | char id[16] = {0}; 710 | snprintf(id, sizeof(id) - 1, "%04hx/", udp_nodes2id.put(string(sin, sinlen))); 711 | 712 | // Note here that ID needs to be appended, unlike with TCP. 713 | fd2state[pt.master()].obuf += psc->possibly_b64encrypt("C:U:S:", fd2state[i].rnode + id + string(sbuf, r)); 714 | pfds[pt.master()].events |= POLLOUT; 715 | 716 | fd2state[i].time = now; 717 | 718 | udp_nodes2sock[fd2state[i].rnode + id] = i; 719 | 720 | } else if (fd2state[i].state == STATE_SCRIPT_ACCEPT) { 721 | 722 | if ((script_fd = accept(i, nullptr, nullptr)) < 0) 723 | continue; 724 | 725 | pfds[script_fd].fd = script_fd; 726 | 727 | pfds[script_fd].events = POLLIN; 728 | pfds[i].events = 0; // block further connects to UNIX script socket 729 | pfds[0].events = 0; // block stdin typing during script processing 730 | 731 | fd2state[script_fd].fd = script_fd; 732 | fd2state[script_fd].rnode = ""; 733 | fd2state[script_fd].state = STATE_SCRIPT_IO; 734 | fd2state[script_fd].time = now; 735 | fd2state[script_fd].obuf.clear(); 736 | 737 | } else if (fd2state[i].state == STATE_SCRIPT_IO) { // very similar to STDIN read 738 | 739 | if ((r = read(i, sbuf, sizeof(sbuf))) <= 0) { 740 | if (errno == EINTR) 741 | continue; 742 | close(i); 743 | script_fd = -1; 744 | pfds[i].fd = -1; 745 | pfds[i].events = 0; 746 | fd2state[i].state = STATE_INVALID; 747 | fd2state[i].fd = -1; 748 | fd2state[i].obuf.clear(); 749 | pfds[0].events |= POLLIN; // reactivate stdin 750 | pfds[config::script_sock].events |= POLLIN; 751 | continue; 752 | } 753 | fd2state[pt.master()].obuf += psc->possibly_b64encrypt("D:0:", string(sbuf, r)); 754 | pfds[pt.master()].events |= POLLOUT; 755 | } 756 | 757 | } else if (pfds[i].revents & POLLOUT) { 758 | pfds[i].revents = 0; 759 | size_t n = fd2state[i].obuf.size() > CHUNK_SIZE ? CHUNK_SIZE : fd2state[i].obuf.size(); 760 | 761 | if (fd2state[i].state == STATE_STDOUT) { 762 | 763 | if ((r = write(1, fd2state[i].obuf.c_str(), n)) <= 0) { 764 | if (errno == EINTR) 765 | continue; 766 | else 767 | die("pscl: write"); 768 | } 769 | 770 | fd2state[i].obuf.erase(0, r); 771 | } else if (fd2state[i].state == STATE_PTY) { 772 | 773 | // We need to throttle amount of data/usec in cases where bounce command was 774 | // given since remote pty is in raw mode w/o flow control or we run across a serial 775 | // line that has a baud rate set. 776 | if (config::rate_limit_bytes) { 777 | n = 1; 778 | 779 | timeval now_tv; 780 | gettimeofday(&now_tv, nullptr); 781 | 782 | // In usec. 783 | uint64_t tdiff_usec = now_tv.tv_sec*1000000 + now_tv.tv_usec - (last_tv.tv_sec*1000000 + last_tv.tv_usec); 784 | 785 | // This formula only works for rates < 1.000.000 Byte/sec which is guaranteed by the baudrates that we accept. 786 | if (tdiff_usec < (1000000*1.0/config::rate_limit_bytes)) 787 | continue; 788 | #if RESPECT_UART_BUFSIZE 789 | // Try to give UART buffers time to get flushed. 790 | if (++tx_rate_cnt >= RESPECT_UART_BUFSIZE) { 791 | if (tdiff_usec < 1000000) 792 | continue; 793 | else 794 | tx_rate_cnt = 0; 795 | } 796 | #endif 797 | 798 | last_tv.tv_sec = now_tv.tv_sec; 799 | last_tv.tv_usec = now_tv.tv_usec; 800 | 801 | // It fits into limit, go ahead with writing. 802 | } 803 | 804 | if ((r = write(psc->w_fileno(), fd2state[i].obuf.c_str(), n)) <= 0) { 805 | if (errno == EAGAIN || errno == EWOULDBLOCK) 806 | continue; 807 | else 808 | die(psc->why()); 809 | } 810 | 811 | 812 | // If we are bouncing binary data to a remote cmd, make some nice local progress output. 813 | if (bcmd_accept_fd >= 0 && fd2state[bcmd_accept_fd].state == STATE_BCMD_CONNECTED) { 814 | bcmd_out_log_str += fd2state[i].obuf.substr(0, r); 815 | 816 | if (bcmd_out_log_str.size() >= 75) { 817 | fd2state[1].obuf += "\r\n> " + trim_bcmd_output(bcmd_out_log_str); 818 | bcmd_out_log_str.clear(); 819 | pfds[1].events |= POLLOUT; 820 | } 821 | } 822 | 823 | fd2state[i].time = now; 824 | fd2state[i].obuf.erase(0, r); 825 | } else if (fd2state[i].state == STATE_CONNECT || // for the SOCKS4/5 case: reply with conn success 826 | fd2state[i].state == STATE_SOCKS5_AUTH2 || // for the SOCKS5 case: reply for auth success 827 | fd2state[i].state == STATE_CONNECTED) { 828 | if ((r = write(i, fd2state[i].obuf.c_str(), n)) <= 0) { 829 | close(i); 830 | pfds[i].fd = -1; 831 | pfds[i].events = 0; 832 | fd2state[i].state = STATE_INVALID; 833 | fd2state[i].fd = -1; 834 | fd2state[i].obuf.clear(); 835 | tcp_nodes2sock.erase(fd2state[i].rnode); 836 | 837 | pfds[pt.master()].events |= POLLOUT; 838 | fd2state[pt.master()].obuf += psc->possibly_b64encrypt("C:T:F:", fd2state[i].rnode); // signal finished connection via PTY to remote 839 | continue; 840 | } 841 | 842 | fd2state[i].time = now; 843 | fd2state[i].obuf.erase(0, r); 844 | } else if (fd2state[i].state == STATE_BCMD_CONNECTED) { 845 | if ((r = write(i, fd2state[i].obuf.c_str(), n)) <= 0) { 846 | close(i); 847 | pfds[i].fd = -1; 848 | pfds[i].events = 0; 849 | fd2state[i].state = STATE_INVALID; 850 | fd2state[i].fd = -1; 851 | fd2state[i].obuf.clear(); 852 | 853 | pfds[pt.master()].events |= POLLOUT; 854 | pfds[0].events |= POLLIN; // reactivate stdin 855 | continue; 856 | } 857 | 858 | fd2state[i].time = now; 859 | fd2state[i].obuf.erase(0, r); 860 | } else if (fd2state[i].state == STATE_UDPSERVER) { 861 | string &dgram = fd2state[i].odgrams.front().second; 862 | string sin = udp_nodes2id.get(fd2state[i].odgrams.front().first); // map id back to originating sockaddr 863 | if ((r = sendto(i, dgram.c_str(), dgram.size(), 0, reinterpret_cast(sin.c_str()), sin.size())) <= 0) 864 | continue; 865 | 866 | fd2state[i].odgrams.pop_front(); 867 | fd2state[i].time = now; 868 | 869 | } else if (fd2state[i].state == STATE_SCRIPT_IO) { 870 | 871 | if ((r = write(i, fd2state[i].obuf.c_str(), n)) <= 0) { 872 | if (errno == EINTR) 873 | continue; 874 | 875 | close(i); 876 | script_fd = -1; 877 | pfds[i].fd = -1; 878 | pfds[i].events = 0; 879 | fd2state[i].state = STATE_INVALID; 880 | fd2state[i].fd = -1; 881 | fd2state[i].obuf.clear(); 882 | pfds[0].events |= POLLIN; // reactivate stdin 883 | pfds[config::script_sock].events |= POLLIN; 884 | continue; 885 | } 886 | 887 | fd2state[i].time = now; 888 | fd2state[i].obuf.erase(0, r); 889 | } 890 | 891 | if (fd2state[i].obuf.empty() && fd2state[i].odgrams.empty()) 892 | pfds[i].events &= ~POLLOUT; 893 | } 894 | } 895 | } 896 | 897 | return 0; 898 | } 899 | 900 | 901 | int main(int argc, char **argv) 902 | { 903 | printf("\nPortShellCrypter [pscl] v0.69 (C) 2006-2024 stealth -- github.com/stealth/psc\n\n"); 904 | 905 | if (!getenv("SHELL")) { 906 | printf("pscl: No $SHELL set in environment. Exiting.\n"); 907 | exit(1); 908 | } 909 | 910 | struct sigaction sa; 911 | memset(&sa, 0, sizeof(sa)); 912 | sa.sa_handler = sig_chld; 913 | sa.sa_flags = SA_RESTART; 914 | 915 | if (sigaction(SIGCHLD, &sa, nullptr) < 0) 916 | die("pscl: sigaction"); 917 | 918 | memset(&sa, 0, sizeof(sa)); 919 | sa.sa_flags = SA_RESTART; 920 | sa.sa_handler = sig_usr1; 921 | if (sigaction(SIGUSR1, &sa, nullptr) < 0) 922 | die("pscl: sigaction"); 923 | 924 | memset(&sa, 0, sizeof(sa)); 925 | sa.sa_flags = SA_RESTART; 926 | sa.sa_handler = sig_usr2; 927 | if (sigaction(SIGUSR2, &sa, nullptr) < 0) 928 | die("pscl: sigaction"); 929 | 930 | memset(&sa, 0, sizeof(sa)); 931 | sa.sa_handler = sig_win; 932 | sa.sa_flags = SA_RESTART; 933 | if (sigaction(SIGWINCH, &sa, nullptr) < 0) 934 | die("pscl: sigaction"); 935 | 936 | sa.sa_handler = SIG_IGN; 937 | if (sigaction(SIGINT, &sa, nullptr) < 0 || sigaction(SIGQUIT, &sa, nullptr) || 938 | sigaction(SIGPIPE, &sa, nullptr)) 939 | die("pscl: sigaction"); 940 | 941 | int c = -1; 942 | char lport[16] = {0}, ip[128] = {0}, port_hex[16] = {0}; 943 | char bounce_cmd[128] = {0}; 944 | uint16_t rport = 0; 945 | string bauds = ""; 946 | bool rate_limit_set = 0; 947 | 948 | while ((c = getopt(argc, argv, "T:U:X:5:4:S:B:l:hN")) != -1) { 949 | switch (c) { 950 | case 'N': 951 | config::socks5_dns = 1; 952 | break; 953 | case 'T': 954 | if (sscanf(optarg, "%15[0-9]:[%127[^]]]:%hu", lport, ip, &rport) == 3) { 955 | snprintf(port_hex, sizeof(port_hex), "%04hx", rport); 956 | config::tcp_listens[lport] = string(ip) + "/" + string(port_hex) + "/"; 957 | printf("pscl: set up local TCP port %s to proxy to %s:%hu @ remote.\n", lport, ip, rport); 958 | } 959 | break; 960 | case 'U': 961 | if (sscanf(optarg, "%15[0-9]:[%127[^]]]:%hu", lport, ip, &rport) == 3) { 962 | snprintf(port_hex, sizeof(port_hex), "%04hx", rport); 963 | config::udp_listens[lport] = string(ip) + "/" + string(port_hex) + "/"; 964 | printf("pscl: set up local UDP port %s to proxy to %s:%hu @ remote.\n", lport, ip, rport); 965 | } 966 | break; 967 | case 'X': 968 | config::local_proxy_ip = optarg; 969 | break; 970 | case '4': 971 | if (config::socks4_fd == -1) { 972 | config::socks4_port = strtoul(optarg, nullptr, 10); 973 | if ((config::socks4_fd = tcp_listen(config::local_proxy_ip, optarg)) > 0) 974 | printf("pscl: set up SOCKS4 port on %s\n", optarg); 975 | } 976 | break; 977 | case '5': 978 | if (config::socks5_fd == -1) { 979 | config::socks5_port = strtoul(optarg, nullptr, 10); 980 | if ((config::socks5_fd = tcp_listen(config::local_proxy_ip, optarg)) > 0) 981 | printf("pscl: set up SOCKS5 port on %s\n", optarg); 982 | } 983 | break; 984 | case 'S': 985 | if (config::script_sock == -1) { 986 | if ((config::script_sock = unix_listen(optarg)) > 0) 987 | printf("pscl: set up script socket on %s\n", optarg);; 988 | } 989 | break; 990 | case 'B': 991 | if (sscanf(optarg, "%15[0-9]:[%127[^]]]", lport, bounce_cmd) == 2) { 992 | config::bcmd_tcp_listens[lport] = bounce_cmd; 993 | printf("pscl: set up local TCP port %s to bounce via `%s` @ remote.\n", lport, bounce_cmd); 994 | } 995 | break; 996 | case 'l': 997 | if (config_set_baud_limit(optarg) < 0) 998 | printf("pscl: Invalid baud rate. Must be one of 576000, 230400, 115200, 57600,\n" 999 | "pscl: 38400, 9600 or 0.\n"); 1000 | else 1001 | rate_limit_set = 1; 1002 | break; 1003 | case 'h': 1004 | default: 1005 | usage(argv[0]); 1006 | exit(0); 1007 | } 1008 | } 1009 | 1010 | // If using bounce cmds, set a default baud rate if none was given 1011 | if (config::bcmd_tcp_listens.size() > 0 && !rate_limit_set) 1012 | config::rate_limit_bytes = 115200/8; 1013 | 1014 | printf("\npscl: Waiting for [pscr] session to appear ...\n"); 1015 | 1016 | proxy_loop(); 1017 | 1018 | return 0; 1019 | } 1020 | 1021 | -------------------------------------------------------------------------------- /src/external/aes.cc: -------------------------------------------------------------------------------- 1 | // Taken from https://github.com/kokke/tiny-AES-C which is under 2 | // public domain and heavily tailored to my needs 3 | 4 | #include 5 | #include "aes.h" 6 | 7 | // The number of columns comprising a state in AES. This is a constant in AES. Value=4 8 | #define Nb 4 9 | 10 | #if defined(AES256) && (AES256 == 1) 11 | #define Nk 8 12 | #define Nr 14 13 | #elif defined(AES192) && (AES192 == 1) 14 | #define Nk 6 15 | #define Nr 12 16 | #else 17 | #define Nk 4 // The number of 32 bit words in a key. 18 | #define Nr 10 // The number of rounds in AES Cipher. 19 | #endif 20 | 21 | 22 | // state - array holding the intermediate results during decryption. 23 | typedef uint8_t state_t[4][4]; 24 | 25 | 26 | // The lookup-tables are marked const so they can be placed in read-only storage instead of RAM 27 | // The numbers below can be computed dynamically trading ROM for RAM - 28 | // This can be useful in (embedded) bootloader applications, where ROM is often limited. 29 | static const uint8_t sbox[256] = { 30 | //0 1 2 3 4 5 6 7 8 9 A B C D E F 31 | 0x63, 0x7c, 0x77, 0x7b, 0xf2, 0x6b, 0x6f, 0xc5, 0x30, 0x01, 0x67, 0x2b, 0xfe, 0xd7, 0xab, 0x76, 32 | 0xca, 0x82, 0xc9, 0x7d, 0xfa, 0x59, 0x47, 0xf0, 0xad, 0xd4, 0xa2, 0xaf, 0x9c, 0xa4, 0x72, 0xc0, 33 | 0xb7, 0xfd, 0x93, 0x26, 0x36, 0x3f, 0xf7, 0xcc, 0x34, 0xa5, 0xe5, 0xf1, 0x71, 0xd8, 0x31, 0x15, 34 | 0x04, 0xc7, 0x23, 0xc3, 0x18, 0x96, 0x05, 0x9a, 0x07, 0x12, 0x80, 0xe2, 0xeb, 0x27, 0xb2, 0x75, 35 | 0x09, 0x83, 0x2c, 0x1a, 0x1b, 0x6e, 0x5a, 0xa0, 0x52, 0x3b, 0xd6, 0xb3, 0x29, 0xe3, 0x2f, 0x84, 36 | 0x53, 0xd1, 0x00, 0xed, 0x20, 0xfc, 0xb1, 0x5b, 0x6a, 0xcb, 0xbe, 0x39, 0x4a, 0x4c, 0x58, 0xcf, 37 | 0xd0, 0xef, 0xaa, 0xfb, 0x43, 0x4d, 0x33, 0x85, 0x45, 0xf9, 0x02, 0x7f, 0x50, 0x3c, 0x9f, 0xa8, 38 | 0x51, 0xa3, 0x40, 0x8f, 0x92, 0x9d, 0x38, 0xf5, 0xbc, 0xb6, 0xda, 0x21, 0x10, 0xff, 0xf3, 0xd2, 39 | 0xcd, 0x0c, 0x13, 0xec, 0x5f, 0x97, 0x44, 0x17, 0xc4, 0xa7, 0x7e, 0x3d, 0x64, 0x5d, 0x19, 0x73, 40 | 0x60, 0x81, 0x4f, 0xdc, 0x22, 0x2a, 0x90, 0x88, 0x46, 0xee, 0xb8, 0x14, 0xde, 0x5e, 0x0b, 0xdb, 41 | 0xe0, 0x32, 0x3a, 0x0a, 0x49, 0x06, 0x24, 0x5c, 0xc2, 0xd3, 0xac, 0x62, 0x91, 0x95, 0xe4, 0x79, 42 | 0xe7, 0xc8, 0x37, 0x6d, 0x8d, 0xd5, 0x4e, 0xa9, 0x6c, 0x56, 0xf4, 0xea, 0x65, 0x7a, 0xae, 0x08, 43 | 0xba, 0x78, 0x25, 0x2e, 0x1c, 0xa6, 0xb4, 0xc6, 0xe8, 0xdd, 0x74, 0x1f, 0x4b, 0xbd, 0x8b, 0x8a, 44 | 0x70, 0x3e, 0xb5, 0x66, 0x48, 0x03, 0xf6, 0x0e, 0x61, 0x35, 0x57, 0xb9, 0x86, 0xc1, 0x1d, 0x9e, 45 | 0xe1, 0xf8, 0x98, 0x11, 0x69, 0xd9, 0x8e, 0x94, 0x9b, 0x1e, 0x87, 0xe9, 0xce, 0x55, 0x28, 0xdf, 46 | 0x8c, 0xa1, 0x89, 0x0d, 0xbf, 0xe6, 0x42, 0x68, 0x41, 0x99, 0x2d, 0x0f, 0xb0, 0x54, 0xbb, 0x16 47 | }; 48 | 49 | // The round constant word array, Rcon[i], contains the values given by 50 | // x to the power (i-1) being powers of x (x is denoted as {02}) in the field GF(2^8) 51 | static const uint8_t Rcon[11] = { 52 | 0x8d, 0x01, 0x02, 0x04, 0x08, 0x10, 0x20, 0x40, 0x80, 0x1b, 0x36 53 | }; 54 | 55 | /* 56 | * Jordan Goulder points out in PR #12 (https://github.com/kokke/tiny-AES-C/pull/12), 57 | * that you can remove most of the elements in the Rcon array, because they are unused. 58 | * 59 | * From Wikipedia's article on the Rijndael key schedule @ https://en.wikipedia.org/wiki/Rijndael_key_schedule#Rcon 60 | * 61 | * "Only the first some of these constants are actually used – up to rcon[10] for AES-128 (as 11 round keys are needed), 62 | * up to rcon[8] for AES-192, up to rcon[7] for AES-256. rcon[0] is not used in AES algorithm." 63 | */ 64 | 65 | #define getSBoxValue(num) (sbox[(num)]) 66 | 67 | // This function produces Nb(Nr+1) round keys. The round keys are used in each round to decrypt the states. 68 | static void 69 | KeyExpansion(uint8_t *RoundKey, const uint8_t *Key) 70 | { 71 | unsigned i, j, k; 72 | uint8_t tempa[4] = {0}; // Used for the column/row operations 73 | 74 | // The first round key is the key itself. 75 | for (i = 0; i < Nk; ++i) { 76 | RoundKey[(i * 4) + 0] = Key[(i * 4) + 0]; 77 | RoundKey[(i * 4) + 1] = Key[(i * 4) + 1]; 78 | RoundKey[(i * 4) + 2] = Key[(i * 4) + 2]; 79 | RoundKey[(i * 4) + 3] = Key[(i * 4) + 3]; 80 | } 81 | 82 | // All other round keys are found from the previous round keys. 83 | for (i = Nk; i < Nb * (Nr + 1); ++i) { 84 | { 85 | k = (i - 1) * 4; 86 | tempa[0] = RoundKey[k + 0]; 87 | tempa[1] = RoundKey[k + 1]; 88 | tempa[2] = RoundKey[k + 2]; 89 | tempa[3] = RoundKey[k + 3]; 90 | 91 | } 92 | 93 | if (i % Nk == 0) { 94 | // This function shifts the 4 bytes in a word to the left once. 95 | // [a0,a1,a2,a3] becomes [a1,a2,a3,a0] 96 | 97 | // Function RotWord() 98 | { 99 | const uint8_t u8tmp = tempa[0]; 100 | tempa[0] = tempa[1]; 101 | tempa[1] = tempa[2]; 102 | tempa[2] = tempa[3]; 103 | tempa[3] = u8tmp; 104 | } 105 | 106 | // SubWord() is a function that takes a four-byte input word and 107 | // applies the S-box to each of the four bytes to produce an output word. 108 | 109 | // Function Subword() 110 | { 111 | tempa[0] = getSBoxValue(tempa[0]); 112 | tempa[1] = getSBoxValue(tempa[1]); 113 | tempa[2] = getSBoxValue(tempa[2]); 114 | tempa[3] = getSBoxValue(tempa[3]); 115 | } 116 | 117 | tempa[0] = tempa[0] ^ Rcon[i / Nk]; 118 | } 119 | #if defined(AES256) && (AES256 == 1) 120 | if (i % Nk == 4) { 121 | // Function Subword() 122 | { 123 | tempa[0] = getSBoxValue(tempa[0]); 124 | tempa[1] = getSBoxValue(tempa[1]); 125 | tempa[2] = getSBoxValue(tempa[2]); 126 | tempa[3] = getSBoxValue(tempa[3]); 127 | } 128 | } 129 | #endif 130 | j = i * 4; 131 | k = (i - Nk) * 4; 132 | RoundKey[j + 0] = RoundKey[k + 0] ^ tempa[0]; 133 | RoundKey[j + 1] = RoundKey[k + 1] ^ tempa[1]; 134 | RoundKey[j + 2] = RoundKey[k + 2] ^ tempa[2]; 135 | RoundKey[j + 3] = RoundKey[k + 3] ^ tempa[3]; 136 | } 137 | } 138 | 139 | 140 | void AES_init_ctx(struct AES_ctx *ctx, const uint8_t *key) 141 | { 142 | memset(ctx, 0, sizeof(*ctx)); 143 | KeyExpansion(ctx->RoundKey, key); 144 | } 145 | 146 | 147 | void AES_init_ctx_iv(struct AES_ctx *ctx, const uint8_t *key, const uint8_t *iv) 148 | { 149 | memset(ctx, 0, sizeof(*ctx)); 150 | KeyExpansion(ctx->RoundKey, key); 151 | memcpy(&ctx->iv, iv, AES_BLOCKLEN); 152 | 153 | //only if 32bit counter is used within IV 154 | //ctx->iv.ctr32 = 0; 155 | } 156 | 157 | 158 | void AES_ctx_set_iv(struct AES_ctx *ctx, const uint8_t *iv) 159 | { 160 | memcpy(&ctx->iv, iv, AES_BLOCKLEN); 161 | 162 | //only if 32bit counter is used within IV 163 | //ctx->iv.ctr32 = 0; 164 | } 165 | 166 | 167 | // This function adds the round key to state. 168 | // The round key is added to the state by an XOR function. 169 | static void AddRoundKey(uint8_t round, state_t *state, const uint8_t *RoundKey) 170 | { 171 | uint8_t i, j; 172 | for (i = 0; i < 4; ++i) { 173 | for (j = 0; j < 4; ++j) { 174 | (*state)[i][j] ^= RoundKey[(round * Nb * 4) + (i * Nb) + j]; 175 | } 176 | } 177 | } 178 | 179 | // The SubBytes Function Substitutes the values in the 180 | // state matrix with values in an S-box. 181 | static void SubBytes(state_t *state) 182 | { 183 | uint8_t i, j; 184 | for (i = 0; i < 4; ++i) { 185 | for (j = 0; j < 4; ++j) { 186 | (*state)[j][i] = getSBoxValue((*state)[j][i]); 187 | } 188 | } 189 | } 190 | 191 | // The ShiftRows() function shifts the rows in the state to the left. 192 | // Each row is shifted with different offset. 193 | // Offset = Row number. So the first row is not shifted. 194 | static void ShiftRows(state_t *state) 195 | { 196 | uint8_t temp; 197 | 198 | // Rotate first row 1 columns to left 199 | temp = (*state)[0][1]; 200 | (*state)[0][1] = (*state)[1][1]; 201 | (*state)[1][1] = (*state)[2][1]; 202 | (*state)[2][1] = (*state)[3][1]; 203 | (*state)[3][1] = temp; 204 | 205 | // Rotate second row 2 columns to left 206 | temp = (*state)[0][2]; 207 | (*state)[0][2] = (*state)[2][2]; 208 | (*state)[2][2] = temp; 209 | 210 | temp = (*state)[1][2]; 211 | (*state)[1][2] = (*state)[3][2]; 212 | (*state)[3][2] = temp; 213 | 214 | // Rotate third row 3 columns to left 215 | temp = (*state)[0][3]; 216 | (*state)[0][3] = (*state)[3][3]; 217 | (*state)[3][3] = (*state)[2][3]; 218 | (*state)[2][3] = (*state)[1][3]; 219 | (*state)[1][3] = temp; 220 | } 221 | 222 | static uint8_t xtime(uint8_t x) 223 | { 224 | return ((x << 1) ^ (((x >> 7) & 1) * 0x1b)); 225 | } 226 | 227 | // MixColumns function mixes the columns of the state matrix 228 | static void MixColumns(state_t *state) 229 | { 230 | uint8_t i; 231 | uint8_t Tmp, Tm, t; 232 | for (i = 0; i < 4; ++i) { 233 | t = (*state)[i][0]; 234 | Tmp = (*state)[i][0] ^ (*state)[i][1] ^ (*state)[i][2] ^ 235 | (*state)[i][3]; 236 | Tm = (*state)[i][0] ^ (*state)[i][1]; 237 | Tm = xtime(Tm); 238 | (*state)[i][0] ^= Tm ^ Tmp; 239 | Tm = (*state)[i][1] ^ (*state)[i][2]; 240 | Tm = xtime(Tm); 241 | (*state)[i][1] ^= Tm ^ Tmp; 242 | Tm = (*state)[i][2] ^ (*state)[i][3]; 243 | Tm = xtime(Tm); 244 | (*state)[i][2] ^= Tm ^ Tmp; 245 | Tm = (*state)[i][3] ^ t; 246 | Tm = xtime(Tm); 247 | (*state)[i][3] ^= Tm ^ Tmp; 248 | } 249 | } 250 | 251 | // Multiply is used to multiply numbers in the field GF(2^8) 252 | #define Multiply(x, y) \ 253 | ( ((y & 1) * x) ^ \ 254 | ((y>>1 & 1) * xtime(x)) ^ \ 255 | ((y>>2 & 1) * xtime(xtime(x))) ^ \ 256 | ((y>>3 & 1) * xtime(xtime(xtime(x)))) ^ \ 257 | ((y>>4 & 1) * xtime(xtime(xtime(xtime(x)))))) 258 | 259 | 260 | 261 | // Cipher is the main function that encrypts the PlainText. 262 | static void Cipher(state_t *state, const uint8_t *RoundKey) 263 | { 264 | uint8_t round = 0; 265 | 266 | // Add the First round key to the state before starting the rounds. 267 | AddRoundKey(0, state, RoundKey); 268 | 269 | // There will be Nr rounds. 270 | // The first Nr-1 rounds are identical. 271 | // These Nr rounds are executed in the loop below. 272 | // Last one without MixColumns() 273 | for (round = 1;; ++round) { 274 | SubBytes(state); 275 | ShiftRows(state); 276 | if (round == Nr) { 277 | break; 278 | } 279 | MixColumns(state); 280 | AddRoundKey(round, state, RoundKey); 281 | } 282 | // Add round key to last round 283 | AddRoundKey(Nr, state, RoundKey); 284 | } 285 | 286 | 287 | // This function was taken from cifra public domain code. 288 | // Increments the integer stored at v (of non-zero length len) 289 | // with the most significant byte last. 290 | static inline void incr_be(uint8_t *v, size_t len) 291 | { 292 | len--; 293 | while (1) { 294 | if (++v[len] != 0) 295 | return; 296 | if (len == 0) 297 | return; 298 | len--; 299 | } 300 | } 301 | 302 | 303 | void AES_CTR_xcrypt(struct AES_ctx *ctx, const uint8_t *in, size_t inlen, uint8_t *out) 304 | { 305 | size_t i = 0; 306 | 307 | // remaining XOR from possibly hang-over from last call 308 | for (; ctx->xidx != 0 && i < inlen;) { 309 | out[i] = in[i] ^ ctx->xor_block[ctx->xidx]; 310 | ctx->xidx = (ctx->xidx + 1) % AES_BLOCKLEN; 311 | ++i; 312 | 313 | // increase ctr for next block 314 | if (ctx->xidx == 0) 315 | incr_be(ctx->iv.iv, AES_BLOCKLEN); 316 | } 317 | 318 | for (; i < inlen;) { 319 | 320 | memcpy(ctx->xor_block, &ctx->iv, AES_BLOCKLEN); 321 | Cipher(reinterpret_cast(ctx->xor_block), ctx->RoundKey); 322 | 323 | for (;i < inlen;) { 324 | out[i] = in[i] ^ ctx->xor_block[ctx->xidx]; 325 | ctx->xidx = (ctx->xidx + 1) % AES_BLOCKLEN; 326 | ++i; 327 | 328 | // increase ctr for next block 329 | if (ctx->xidx == 0) { 330 | incr_be(ctx->iv.iv, AES_BLOCKLEN); 331 | break; 332 | } 333 | } 334 | } 335 | } 336 | 337 | -------------------------------------------------------------------------------- /src/external/aes.h: -------------------------------------------------------------------------------- 1 | #ifndef _AES_H_ 2 | #define _AES_H_ 3 | 4 | #include 5 | 6 | #define AES256 1 7 | 8 | #define AES_BLOCKLEN 16 // Block length in bytes - AES is 128b block only 9 | 10 | #if defined(AES256) && (AES256 == 1) 11 | #define AES_KEYLEN 32 12 | #define AES_keyExpSize 240 13 | #elif defined(AES192) && (AES192 == 1) 14 | #define AES_KEYLEN 24 15 | #define AES_keyExpSize 208 16 | #else 17 | #define AES_KEYLEN 16 // Key length in bytes 18 | #define AES_keyExpSize 176 19 | #endif 20 | 21 | struct AES_ctx { 22 | uint8_t RoundKey[AES_keyExpSize]; 23 | struct { 24 | uint8_t iv[AES_BLOCKLEN]; 25 | //uint32_t ctr32; 26 | } iv; 27 | uint8_t xor_block[AES_BLOCKLEN]; 28 | uint8_t xidx; 29 | }; 30 | 31 | void AES_init_ctx(struct AES_ctx *, const uint8_t *); 32 | 33 | void AES_init_ctx_iv(struct AES_ctx *, const uint8_t *, const uint8_t *); 34 | 35 | void AES_ctx_set_iv(struct AES_ctx *, const uint8_t *); 36 | 37 | void AES_CTR_xcrypt(struct AES_ctx *, const uint8_t *, size_t, uint8_t *); 38 | 39 | 40 | #endif // _AES_H_ 41 | -------------------------------------------------------------------------------- /src/external/sha512.cc: -------------------------------------------------------------------------------- 1 | /* 2 | * BIRD Library -- SHA-512 and SHA-384 Hash Functions 3 | * 4 | * (c) 2015 CZ.NIC z.s.p.o. 5 | * 6 | * Based on the code from libgcrypt-1.6.0, which is 7 | * (c) 2003, 2006, 2008, 2009 Free Software Foundation, Inc. 8 | * 9 | * Can be freely distributed and used under the terms of the GNU GPL. 10 | */ 11 | 12 | #include 13 | #include 14 | #include 15 | #include 16 | #include 17 | #include "sha512.h" 18 | 19 | static inline uint64_t 20 | get_u64(const uint8_t * p) 21 | { 22 | uint32_t xh, xl; 23 | memcpy(&xh, p, 4); 24 | memcpy(&xl, p + 4, 4); 25 | return (((uint64_t) ntohl(xh)) << 32) | ntohl(xl); 26 | } 27 | 28 | static inline void 29 | put_u64(uint8_t * p, uint64_t x) 30 | { 31 | uint32_t xh, xl; 32 | xh = htonl(x >> 32); 33 | xl = htonl((uint32_t) x); 34 | memcpy(p, &xh, 4); 35 | memcpy(p + 4, &xl, 4); 36 | } 37 | 38 | 39 | // #define SHA512_UNROLLED 40 | 41 | void 42 | sha512_init(struct sha512_context *ctx) 43 | { 44 | ctx->h0 = UINT64_C(0x6a09e667f3bcc908); 45 | ctx->h1 = UINT64_C(0xbb67ae8584caa73b); 46 | ctx->h2 = UINT64_C(0x3c6ef372fe94f82b); 47 | ctx->h3 = UINT64_C(0xa54ff53a5f1d36f1); 48 | ctx->h4 = UINT64_C(0x510e527fade682d1); 49 | ctx->h5 = UINT64_C(0x9b05688c2b3e6c1f); 50 | ctx->h6 = UINT64_C(0x1f83d9abfb41bd6b); 51 | ctx->h7 = UINT64_C(0x5be0cd19137e2179); 52 | 53 | ctx->nblocks = 0; 54 | ctx->count = 0; 55 | } 56 | 57 | static inline uint64_t 58 | ROTR(uint64_t x, uint64_t n) 59 | { 60 | return ((x >> n) | (x << (64 - n))); 61 | } 62 | 63 | static inline uint64_t 64 | Ch(uint64_t x, uint64_t y, uint64_t z) 65 | { 66 | return ((x & y) ^ (~x & z)); 67 | } 68 | 69 | static inline uint64_t 70 | Maj(uint64_t x, uint64_t y, uint64_t z) 71 | { 72 | return ((x & y) ^ (x & z) ^ (y & z)); 73 | } 74 | 75 | static inline uint64_t 76 | sum0(uint64_t x) 77 | { 78 | return (ROTR(x, 28) ^ ROTR(x, 34) ^ ROTR(x, 39)); 79 | } 80 | 81 | static inline uint64_t 82 | sum1(uint64_t x) 83 | { 84 | return (ROTR(x, 14) ^ ROTR(x, 18) ^ ROTR(x, 41)); 85 | } 86 | 87 | static const uint64_t k[] = { 88 | UINT64_C(0x428a2f98d728ae22), UINT64_C(0x7137449123ef65cd), 89 | UINT64_C(0xb5c0fbcfec4d3b2f), UINT64_C(0xe9b5dba58189dbbc), 90 | UINT64_C(0x3956c25bf348b538), UINT64_C(0x59f111f1b605d019), 91 | UINT64_C(0x923f82a4af194f9b), UINT64_C(0xab1c5ed5da6d8118), 92 | UINT64_C(0xd807aa98a3030242), UINT64_C(0x12835b0145706fbe), 93 | UINT64_C(0x243185be4ee4b28c), UINT64_C(0x550c7dc3d5ffb4e2), 94 | UINT64_C(0x72be5d74f27b896f), UINT64_C(0x80deb1fe3b1696b1), 95 | UINT64_C(0x9bdc06a725c71235), UINT64_C(0xc19bf174cf692694), 96 | UINT64_C(0xe49b69c19ef14ad2), UINT64_C(0xefbe4786384f25e3), 97 | UINT64_C(0x0fc19dc68b8cd5b5), UINT64_C(0x240ca1cc77ac9c65), 98 | UINT64_C(0x2de92c6f592b0275), UINT64_C(0x4a7484aa6ea6e483), 99 | UINT64_C(0x5cb0a9dcbd41fbd4), UINT64_C(0x76f988da831153b5), 100 | UINT64_C(0x983e5152ee66dfab), UINT64_C(0xa831c66d2db43210), 101 | UINT64_C(0xb00327c898fb213f), UINT64_C(0xbf597fc7beef0ee4), 102 | UINT64_C(0xc6e00bf33da88fc2), UINT64_C(0xd5a79147930aa725), 103 | UINT64_C(0x06ca6351e003826f), UINT64_C(0x142929670a0e6e70), 104 | UINT64_C(0x27b70a8546d22ffc), UINT64_C(0x2e1b21385c26c926), 105 | UINT64_C(0x4d2c6dfc5ac42aed), UINT64_C(0x53380d139d95b3df), 106 | UINT64_C(0x650a73548baf63de), UINT64_C(0x766a0abb3c77b2a8), 107 | UINT64_C(0x81c2c92e47edaee6), UINT64_C(0x92722c851482353b), 108 | UINT64_C(0xa2bfe8a14cf10364), UINT64_C(0xa81a664bbc423001), 109 | UINT64_C(0xc24b8b70d0f89791), UINT64_C(0xc76c51a30654be30), 110 | UINT64_C(0xd192e819d6ef5218), UINT64_C(0xd69906245565a910), 111 | UINT64_C(0xf40e35855771202a), UINT64_C(0x106aa07032bbd1b8), 112 | UINT64_C(0x19a4c116b8d2d0c8), UINT64_C(0x1e376c085141ab53), 113 | UINT64_C(0x2748774cdf8eeb99), UINT64_C(0x34b0bcb5e19b48a8), 114 | UINT64_C(0x391c0cb3c5c95a63), UINT64_C(0x4ed8aa4ae3418acb), 115 | UINT64_C(0x5b9cca4f7763e373), UINT64_C(0x682e6ff3d6b2b8a3), 116 | UINT64_C(0x748f82ee5defb2fc), UINT64_C(0x78a5636f43172f60), 117 | UINT64_C(0x84c87814a1f0ab72), UINT64_C(0x8cc702081a6439ec), 118 | UINT64_C(0x90befffa23631e28), UINT64_C(0xa4506cebde82bde9), 119 | UINT64_C(0xbef9a3f7b2c67915), UINT64_C(0xc67178f2e372532b), 120 | UINT64_C(0xca273eceea26619c), UINT64_C(0xd186b8c721c0c207), 121 | UINT64_C(0xeada7dd6cde0eb1e), UINT64_C(0xf57d4f7fee6ed178), 122 | UINT64_C(0x06f067aa72176fba), UINT64_C(0x0a637dc5a2c898a6), 123 | UINT64_C(0x113f9804bef90dae), UINT64_C(0x1b710b35131c471b), 124 | UINT64_C(0x28db77f523047d84), UINT64_C(0x32caab7b40c72493), 125 | UINT64_C(0x3c9ebe0a15c9bebc), UINT64_C(0x431d67c49c100d4c), 126 | UINT64_C(0x4cc5d4becb3e42b6), UINT64_C(0x597f299cfc657e2a), 127 | UINT64_C(0x5fcb6fab3ad6faec), UINT64_C(0x6c44198c4a475817) 128 | }; 129 | 130 | /* 131 | * Transform the message W which consists of 16 64-bit-words 132 | */ 133 | static uint32_t 134 | sha512_transform(struct sha512_context *ctx, const uint8_t * data) 135 | { 136 | uint64_t a, b, c, d, e, f, g, h; 137 | uint64_t w[16]; 138 | uint32_t t; 139 | 140 | /* get values from the chaining vars */ 141 | a = ctx->h0; 142 | b = ctx->h1; 143 | c = ctx->h2; 144 | d = ctx->h3; 145 | e = ctx->h4; 146 | f = ctx->h5; 147 | g = ctx->h6; 148 | h = ctx->h7; 149 | 150 | for (t = 0; t < 16; t++) 151 | w[t] = get_u64(data + t * 8); 152 | 153 | #define S0(x) (ROTR((x),1) ^ ROTR((x),8) ^ ((x)>>7)) 154 | #define S1(x) (ROTR((x),19) ^ ROTR((x),61) ^ ((x)>>6)) 155 | 156 | for (t = 0; t < 80 - 16;) { 157 | uint64_t t1, t2; 158 | 159 | /* Performance on a AMD Athlon(tm) Dual Core Processor 4050e 160 | with gcc 4.3.3 using gcry_md_hash_buffer of each 10000 uint8_ts 161 | initialized to 0,1,2,3...255,0,... and 1000 iterations: 162 | 163 | Not unrolled with macros: 440ms 164 | Unrolled with macros: 350ms 165 | Unrolled with inline: 330ms 166 | */ 167 | #ifndef SHA512_UNROLLED 168 | t1 = h + sum1(e) + Ch(e, f, g) + k[t] + w[t % 16]; 169 | w[t % 16] += 170 | S1(w[(t - 2) % 16]) + w[(t - 7) % 16] + 171 | S0(w[(t - 15) % 16]); 172 | t2 = sum0(a) + Maj(a, b, c); 173 | h = g; 174 | g = f; 175 | f = e; 176 | e = d + t1; 177 | d = c; 178 | c = b; 179 | b = a; 180 | a = t1 + t2; 181 | t++; 182 | #else /* Unrolled */ 183 | t1 = h + sum1(e) + Ch(e, f, g) + k[t] + w[0]; 184 | w[0] += S1(w[14]) + w[9] + S0(w[1]); 185 | t2 = sum0(a) + Maj(a, b, c); 186 | d += t1; 187 | h = t1 + t2; 188 | 189 | t1 = g + sum1(d) + Ch(d, e, f) + k[t + 1] + w[1]; 190 | w[1] += S1(w[15]) + w[10] + S0(w[2]); 191 | t2 = sum0(h) + Maj(h, a, b); 192 | c += t1; 193 | g = t1 + t2; 194 | 195 | t1 = f + sum1(c) + Ch(c, d, e) + k[t + 2] + w[2]; 196 | w[2] += S1(w[0]) + w[11] + S0(w[3]); 197 | t2 = sum0(g) + Maj(g, h, a); 198 | b += t1; 199 | f = t1 + t2; 200 | 201 | t1 = e + sum1(b) + Ch(b, c, d) + k[t + 3] + w[3]; 202 | w[3] += S1(w[1]) + w[12] + S0(w[4]); 203 | t2 = sum0(f) + Maj(f, g, h); 204 | a += t1; 205 | e = t1 + t2; 206 | 207 | t1 = d + sum1(a) + Ch(a, b, c) + k[t + 4] + w[4]; 208 | w[4] += S1(w[2]) + w[13] + S0(w[5]); 209 | t2 = sum0(e) + Maj(e, f, g); 210 | h += t1; 211 | d = t1 + t2; 212 | 213 | t1 = c + sum1(h) + Ch(h, a, b) + k[t + 5] + w[5]; 214 | w[5] += S1(w[3]) + w[14] + S0(w[6]); 215 | t2 = sum0(d) + Maj(d, e, f); 216 | g += t1; 217 | c = t1 + t2; 218 | 219 | t1 = b + sum1(g) + Ch(g, h, a) + k[t + 6] + w[6]; 220 | w[6] += S1(w[4]) + w[15] + S0(w[7]); 221 | t2 = sum0(c) + Maj(c, d, e); 222 | f += t1; 223 | b = t1 + t2; 224 | 225 | t1 = a + sum1(f) + Ch(f, g, h) + k[t + 7] + w[7]; 226 | w[7] += S1(w[5]) + w[0] + S0(w[8]); 227 | t2 = sum0(b) + Maj(b, c, d); 228 | e += t1; 229 | a = t1 + t2; 230 | 231 | t1 = h + sum1(e) + Ch(e, f, g) + k[t + 8] + w[8]; 232 | w[8] += S1(w[6]) + w[1] + S0(w[9]); 233 | t2 = sum0(a) + Maj(a, b, c); 234 | d += t1; 235 | h = t1 + t2; 236 | 237 | t1 = g + sum1(d) + Ch(d, e, f) + k[t + 9] + w[9]; 238 | w[9] += S1(w[7]) + w[2] + S0(w[10]); 239 | t2 = sum0(h) + Maj(h, a, b); 240 | c += t1; 241 | g = t1 + t2; 242 | 243 | t1 = f + sum1(c) + Ch(c, d, e) + k[t + 10] + w[10]; 244 | w[10] += S1(w[8]) + w[3] + S0(w[11]); 245 | t2 = sum0(g) + Maj(g, h, a); 246 | b += t1; 247 | f = t1 + t2; 248 | 249 | t1 = e + sum1(b) + Ch(b, c, d) + k[t + 11] + w[11]; 250 | w[11] += S1(w[9]) + w[4] + S0(w[12]); 251 | t2 = sum0(f) + Maj(f, g, h); 252 | a += t1; 253 | e = t1 + t2; 254 | 255 | t1 = d + sum1(a) + Ch(a, b, c) + k[t + 12] + w[12]; 256 | w[12] += S1(w[10]) + w[5] + S0(w[13]); 257 | t2 = sum0(e) + Maj(e, f, g); 258 | h += t1; 259 | d = t1 + t2; 260 | 261 | t1 = c + sum1(h) + Ch(h, a, b) + k[t + 13] + w[13]; 262 | w[13] += S1(w[11]) + w[6] + S0(w[14]); 263 | t2 = sum0(d) + Maj(d, e, f); 264 | g += t1; 265 | c = t1 + t2; 266 | 267 | t1 = b + sum1(g) + Ch(g, h, a) + k[t + 14] + w[14]; 268 | w[14] += S1(w[12]) + w[7] + S0(w[15]); 269 | t2 = sum0(c) + Maj(c, d, e); 270 | f += t1; 271 | b = t1 + t2; 272 | 273 | t1 = a + sum1(f) + Ch(f, g, h) + k[t + 15] + w[15]; 274 | w[15] += S1(w[13]) + w[8] + S0(w[0]); 275 | t2 = sum0(b) + Maj(b, c, d); 276 | e += t1; 277 | a = t1 + t2; 278 | 279 | t += 16; 280 | #endif 281 | } 282 | 283 | for (; t < 80;) { 284 | uint64_t t1, t2; 285 | 286 | #ifndef SHA512_UNROLLED 287 | t1 = h + sum1(e) + Ch(e, f, g) + k[t] + w[t % 16]; 288 | t2 = sum0(a) + Maj(a, b, c); 289 | h = g; 290 | g = f; 291 | f = e; 292 | e = d + t1; 293 | d = c; 294 | c = b; 295 | b = a; 296 | a = t1 + t2; 297 | t++; 298 | #else /* Unrolled */ 299 | t1 = h + sum1(e) + Ch(e, f, g) + k[t] + w[0]; 300 | t2 = sum0(a) + Maj(a, b, c); 301 | d += t1; 302 | h = t1 + t2; 303 | 304 | t1 = g + sum1(d) + Ch(d, e, f) + k[t + 1] + w[1]; 305 | t2 = sum0(h) + Maj(h, a, b); 306 | c += t1; 307 | g = t1 + t2; 308 | 309 | t1 = f + sum1(c) + Ch(c, d, e) + k[t + 2] + w[2]; 310 | t2 = sum0(g) + Maj(g, h, a); 311 | b += t1; 312 | f = t1 + t2; 313 | 314 | t1 = e + sum1(b) + Ch(b, c, d) + k[t + 3] + w[3]; 315 | t2 = sum0(f) + Maj(f, g, h); 316 | a += t1; 317 | e = t1 + t2; 318 | 319 | t1 = d + sum1(a) + Ch(a, b, c) + k[t + 4] + w[4]; 320 | t2 = sum0(e) + Maj(e, f, g); 321 | h += t1; 322 | d = t1 + t2; 323 | 324 | t1 = c + sum1(h) + Ch(h, a, b) + k[t + 5] + w[5]; 325 | t2 = sum0(d) + Maj(d, e, f); 326 | g += t1; 327 | c = t1 + t2; 328 | 329 | t1 = b + sum1(g) + Ch(g, h, a) + k[t + 6] + w[6]; 330 | t2 = sum0(c) + Maj(c, d, e); 331 | f += t1; 332 | b = t1 + t2; 333 | 334 | t1 = a + sum1(f) + Ch(f, g, h) + k[t + 7] + w[7]; 335 | t2 = sum0(b) + Maj(b, c, d); 336 | e += t1; 337 | a = t1 + t2; 338 | 339 | t1 = h + sum1(e) + Ch(e, f, g) + k[t + 8] + w[8]; 340 | t2 = sum0(a) + Maj(a, b, c); 341 | d += t1; 342 | h = t1 + t2; 343 | 344 | t1 = g + sum1(d) + Ch(d, e, f) + k[t + 9] + w[9]; 345 | t2 = sum0(h) + Maj(h, a, b); 346 | c += t1; 347 | g = t1 + t2; 348 | 349 | t1 = f + sum1(c) + Ch(c, d, e) + k[t + 10] + w[10]; 350 | t2 = sum0(g) + Maj(g, h, a); 351 | b += t1; 352 | f = t1 + t2; 353 | 354 | t1 = e + sum1(b) + Ch(b, c, d) + k[t + 11] + w[11]; 355 | t2 = sum0(f) + Maj(f, g, h); 356 | a += t1; 357 | e = t1 + t2; 358 | 359 | t1 = d + sum1(a) + Ch(a, b, c) + k[t + 12] + w[12]; 360 | t2 = sum0(e) + Maj(e, f, g); 361 | h += t1; 362 | d = t1 + t2; 363 | 364 | t1 = c + sum1(h) + Ch(h, a, b) + k[t + 13] + w[13]; 365 | t2 = sum0(d) + Maj(d, e, f); 366 | g += t1; 367 | c = t1 + t2; 368 | 369 | t1 = b + sum1(g) + Ch(g, h, a) + k[t + 14] + w[14]; 370 | t2 = sum0(c) + Maj(c, d, e); 371 | f += t1; 372 | b = t1 + t2; 373 | 374 | t1 = a + sum1(f) + Ch(f, g, h) + k[t + 15] + w[15]; 375 | t2 = sum0(b) + Maj(b, c, d); 376 | e += t1; 377 | a = t1 + t2; 378 | 379 | t += 16; 380 | #endif 381 | } 382 | 383 | /* Update chaining vars. */ 384 | ctx->h0 += a; 385 | ctx->h1 += b; 386 | ctx->h2 += c; 387 | ctx->h3 += d; 388 | ctx->h4 += e; 389 | ctx->h5 += f; 390 | ctx->h6 += g; 391 | ctx->h7 += h; 392 | 393 | return /* burn_stack */ (8 + 16) * sizeof(uint64_t) + sizeof(uint32_t) + 394 | 3 * sizeof(void *); 395 | } 396 | 397 | void 398 | sha512_update(struct sha512_context *ctx, const uint8_t * buf, uint32_t len) 399 | { 400 | 401 | if (ctx->count) { 402 | /* Fill rest of internal buffer */ 403 | for (; len && ctx->count < SHA512_BLOCK_SIZE; len--) 404 | ctx->buf[ctx->count++] = *buf++; 405 | 406 | if (ctx->count < SHA512_BLOCK_SIZE) 407 | return; 408 | 409 | /* Process data from internal buffer */ 410 | sha512_transform(ctx, ctx->buf); 411 | ctx->nblocks++; 412 | ctx->count = 0; 413 | } 414 | 415 | if (!len) 416 | return; 417 | 418 | /* Process data from input buffer */ 419 | while (len >= SHA512_BLOCK_SIZE) { 420 | sha512_transform(ctx, buf); 421 | ctx->nblocks++; 422 | buf += SHA512_BLOCK_SIZE; 423 | len -= SHA512_BLOCK_SIZE; 424 | } 425 | 426 | /* Copy remaining data to internal buffer */ 427 | memcpy(ctx->buf, buf, len); 428 | ctx->count = len; 429 | } 430 | 431 | /* 432 | * The routine final terminates the computation and returns the digest. The 433 | * handle is prepared for a new cycle, but adding uint8_ts to the handle will the 434 | * destroy the returned buffer. 435 | * 436 | * Returns: 64 uint8_ts representing the digest. When used for sha384, we take the 437 | * first 48 of those uint8_ts. 438 | */ 439 | uint8_t * 440 | sha512_final(struct sha512_context *ctx) 441 | { 442 | uint64_t t, th, msb, lsb; 443 | 444 | sha512_update(ctx, nullptr, 0); /* flush */ 445 | 446 | t = ctx->nblocks; 447 | th = 0; 448 | 449 | /* multiply by 128 to make a uint8_t count */ 450 | lsb = t << 7; 451 | msb = (th << 7) | (t >> 57); 452 | /* add the count */ 453 | t = lsb; 454 | if ((lsb += ctx->count) < t) 455 | msb++; 456 | /* multiply by 8 to make a bit count */ 457 | t = lsb; 458 | lsb <<= 3; 459 | msb <<= 3; 460 | msb |= t >> 61; 461 | 462 | if (ctx->count < 112) { 463 | /* enough room */ 464 | ctx->buf[ctx->count++] = 0x80; /* pad */ 465 | while (ctx->count < 112) 466 | ctx->buf[ctx->count++] = 0; /* pad */ 467 | } else { 468 | /* need one extra block */ 469 | ctx->buf[ctx->count++] = 0x80; /* pad character */ 470 | while (ctx->count < 128) 471 | ctx->buf[ctx->count++] = 0; 472 | sha512_update(ctx, nullptr, 0); /* flush */ 473 | memset(ctx->buf, 0, 112); /* fill next block with zeroes */ 474 | } 475 | 476 | /* append the 128 bit count */ 477 | put_u64(ctx->buf + 112, msb); 478 | put_u64(ctx->buf + 120, lsb); 479 | sha512_transform(ctx, ctx->buf); 480 | 481 | uint8_t *p = ctx->buf; 482 | #define X(a) do { put_u64(p, ctx->h##a); p += 8; } while(0) 483 | X(0); 484 | X(1); 485 | X(2); 486 | X(3); 487 | X(4); 488 | X(5); 489 | X(6); 490 | X(7); 491 | #undef X 492 | 493 | return ctx->buf; 494 | } 495 | -------------------------------------------------------------------------------- /src/external/sha512.h: -------------------------------------------------------------------------------- 1 | /* 2 | * BIRD Library -- SHA-512 and SHA-384 Hash Functions 3 | * 4 | * (c) 2015 CZ.NIC z.s.p.o. 5 | * 6 | * Based on the code from libgcrypt-1.6.0, which is 7 | * (c) 2003, 2006, 2008, 2009 Free Software Foundation, Inc. 8 | * 9 | * Can be freely distributed and used under the terms of the GNU GPL. 10 | */ 11 | 12 | #ifndef _BIRD_SHA512_H_ 13 | #define _BIRD_SHA512_H_ 14 | 15 | #define SHA512_SIZE 64 16 | #define SHA512_HEX_SIZE 129 17 | #define SHA512_BLOCK_SIZE 128 18 | 19 | #include 20 | 21 | 22 | struct sha512_context { 23 | uint64_t h0, h1, h2, h3, h4, h5, h6, h7; 24 | uint8_t buf[SHA512_BLOCK_SIZE]; 25 | uint32_t nblocks; 26 | uint32_t count; 27 | }; 28 | 29 | 30 | void sha512_init(struct sha512_context *); 31 | 32 | void sha512_update(struct sha512_context *, const uint8_t *, uint32_t); 33 | 34 | uint8_t *sha512_final(struct sha512_context *); 35 | 36 | 37 | #endif /* _BIRD_SHA512_H_ */ 38 | -------------------------------------------------------------------------------- /src/misc.cc: -------------------------------------------------------------------------------- 1 | /* 2 | * This file is part of port shell crypter (psc). 3 | * 4 | * (C) 2006-2022 by Sebastian Krahmer, 5 | * sebastian [dot] krahmer [at] gmail [dot] com 6 | * 7 | * psc is free software: you can redistribute it and/or modify 8 | * it under the terms of the GNU General Public License as published by 9 | * the Free Software Foundation, either version 3 of the License, or 10 | * (at your option) any later version. 11 | * 12 | * psc is distributed in the hope that it will be useful, 13 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 14 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 | * GNU General Public License for more details. 16 | * 17 | * You should have received a copy of the GNU General Public License 18 | * along with psc. If not, see . 19 | */ 20 | 21 | 22 | #include 23 | #include 24 | #include 25 | #include 26 | #include 27 | #include 28 | #include 29 | #include 30 | #include 31 | #include 32 | #include 33 | #include 34 | 35 | 36 | using namespace std; 37 | 38 | 39 | namespace config { 40 | 41 | map tcp_listens, udp_listens, bcmd_tcp_listens; 42 | 43 | int socks5_port = -1, socks5_fd = -1, socks4_port = -1, socks4_fd = -1, script_sock = -1; 44 | 45 | bool socks5_dns = 0; 46 | 47 | string local_proxy_ip = "127.0.0.1"; 48 | 49 | size_t rate_limit_bytes = 0; // default off 50 | 51 | } 52 | 53 | namespace ns_psc { 54 | 55 | extern struct termios exit_tattr; 56 | 57 | 58 | void die(const char *s) 59 | { 60 | fprintf(stderr, "[%d] %s: %s\n", getpid(), s, strerror(errno)); 61 | tcsetattr(0, TCSANOW, &exit_tattr); 62 | exit(errno); 63 | } 64 | 65 | 66 | int config_set_baud_limit(const string &bauds) 67 | { 68 | if (bauds == "576000") 69 | config::rate_limit_bytes = 576000/8; 70 | else if (bauds == "230400") 71 | config::rate_limit_bytes = 230400/8; 72 | else if (bauds == "115200") 73 | config::rate_limit_bytes = 115200/8; 74 | else if (bauds == "57600") 75 | config::rate_limit_bytes = 57600/8; 76 | else if (bauds == "38400") 77 | config::rate_limit_bytes = 38400/8; 78 | else if (bauds == "9600") 79 | config::rate_limit_bytes = 9600/8; 80 | else if (bauds == "0") 81 | config::rate_limit_bytes = 0; 82 | else 83 | return -1; 84 | 85 | return 0; 86 | } 87 | 88 | 89 | void fix_size(int fd) 90 | { 91 | struct winsize win; 92 | 93 | if (ioctl(0, TIOCGWINSZ, (char*)&win) >= 0) 94 | ioctl(fd, TIOCSWINSZ, (char*)&win); 95 | } 96 | 97 | 98 | int writen(int fd, const char *buf, size_t blen) 99 | { 100 | ssize_t r; 101 | size_t n = blen; 102 | 103 | for (int i = 0; n > 0;) { 104 | r = write(fd, buf + i, n); 105 | if (r == 0) 106 | return 0; 107 | if (r < 0) { 108 | if ((errno == EAGAIN || errno == EWOULDBLOCK)) 109 | return i; 110 | return r; 111 | } 112 | i += r; 113 | n -= r; 114 | } 115 | return (int)blen; 116 | } 117 | 118 | 119 | static const char *b64 = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"; 120 | 121 | 122 | /* The base64 routines have been taken from the Samba 3 source (GPL) 123 | * and have been slightly modified */ 124 | /* expects enough space in buf */ 125 | size_t b64_decode(const char *s, unsigned char *buf) 126 | { 127 | int bit_offset, byte_offset, idx, i, n; 128 | unsigned char *d = buf; 129 | const char *p; 130 | 131 | n=i=0; 132 | 133 | while (*s && (p=strchr(b64,*s))) { 134 | idx = (int)(p - b64); 135 | byte_offset = (i*6)/8; 136 | bit_offset = (i*6)%8; 137 | d[byte_offset] &= ~((1<<(8-bit_offset))-1); 138 | if (bit_offset < 3) { 139 | d[byte_offset] |= (idx << (2-bit_offset)); 140 | n = byte_offset+1; 141 | } else { 142 | d[byte_offset] |= (idx >> (bit_offset-2)); 143 | d[byte_offset+1] = 0; 144 | d[byte_offset+1] |= (idx << (8-(bit_offset-2))) & 0xFF; 145 | n = byte_offset+2; 146 | } 147 | s++; i++; 148 | } 149 | 150 | if (*s == '=') n -= 1; 151 | 152 | return n; 153 | } 154 | 155 | 156 | char *b64_encode(const char *s, size_t len, unsigned char *buf) 157 | { 158 | int bits = 0; 159 | int char_count = 0; 160 | size_t out_cnt = 0; 161 | unsigned char *result = buf; 162 | 163 | while (len--) { 164 | int c = (unsigned char) *(s++); 165 | bits += c; 166 | char_count++; 167 | if (char_count == 3) { 168 | result[out_cnt++] = b64[bits >> 18]; 169 | result[out_cnt++] = b64[(bits >> 12) & 0x3f]; 170 | result[out_cnt++] = b64[(bits >> 6) & 0x3f]; 171 | result[out_cnt++] = b64[bits & 0x3f]; 172 | bits = 0; 173 | char_count = 0; 174 | } else { 175 | bits <<= 8; 176 | } 177 | } 178 | if (char_count != 0) { 179 | bits <<= 16 - (8 * char_count); 180 | result[out_cnt++] = b64[bits >> 18]; 181 | result[out_cnt++] = b64[(bits >> 12) & 0x3f]; 182 | if (char_count == 1) { 183 | result[out_cnt++] = '='; 184 | result[out_cnt++] = '='; 185 | } else { 186 | result[out_cnt++] = b64[(bits >> 6) & 0x3f]; 187 | result[out_cnt++] = '='; 188 | } 189 | } 190 | result[out_cnt] = '\0'; /* terminate */ 191 | return reinterpret_cast(result); 192 | } 193 | 194 | 195 | // our own limited version, as we only need few bytes 196 | int RAND_bytes(unsigned char *buf, int num) 197 | { 198 | int fd = 0, r = 0; 199 | 200 | if ((fd = open("/dev/urandom", O_RDONLY|O_NOCTTY)) < 0) 201 | return 0; 202 | if (read(fd, buf, num) == num) 203 | r = 1; 204 | close(fd); 205 | 206 | return r; 207 | } 208 | 209 | 210 | } 211 | 212 | -------------------------------------------------------------------------------- /src/misc.h: -------------------------------------------------------------------------------- 1 | /* 2 | * This file is part of port shell crypter (psc). 3 | * 4 | * (C) 2006-2021 by Sebastian Krahmer, 5 | * sebastian [dot] krahmer [at] gmail [dot] com 6 | * 7 | * psc is free software: you can redistribute it and/or modify 8 | * it under the terms of the GNU General Public License as published by 9 | * the Free Software Foundation, either version 3 of the License, or 10 | * (at your option) any later version. 11 | * 12 | * psc is distributed in the hope that it will be useful, 13 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 14 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 | * GNU General Public License for more details. 16 | * 17 | * You should have received a copy of the GNU General Public License 18 | * along with psc. If not, see . 19 | */ 20 | 21 | #ifndef psc_misc_h 22 | #define psc_misc_h 23 | 24 | #include 25 | #include 26 | #include 27 | #include 28 | #include 29 | #include 30 | #include 31 | 32 | namespace ns_psc { 33 | 34 | void die(const char *); 35 | 36 | int config_set_baud_limit(const std::string &); 37 | 38 | void fix_size(int); 39 | 40 | int writen(int, const char *, size_t); 41 | 42 | size_t b64_decode(const char *, unsigned char *); 43 | 44 | char *b64_encode(const char *, size_t, unsigned char *); 45 | 46 | int RAND_bytes(unsigned char *, int); 47 | 48 | enum { 49 | 50 | STATE_INVALID = 0, 51 | STATE_PTY = 1, 52 | STATE_STDIN = 2, 53 | STATE_STDOUT = 3, 54 | STATE_ACCEPT = 4, 55 | STATE_CONNECT = 5, 56 | STATE_CONNECTED = 6, 57 | STATE_CLOSING = 7, 58 | STATE_UDPCLIENT = 8, 59 | STATE_UDPSERVER = 9, 60 | STATE_SOCKS5_ACCEPT = 10, 61 | STATE_SOCKS5_AUTH1 = 11, 62 | STATE_SOCKS5_AUTH2 = 12, 63 | STATE_SOCKS4_ACCEPT = 13, 64 | STATE_SOCKS4_AUTH = 14, 65 | STATE_SCRIPT_ACCEPT = 15, 66 | STATE_SCRIPT_IO = 16, 67 | STATE_BCMD_ACCEPT = 17, 68 | STATE_BCMD_CONNECT = 18, 69 | STATE_BCMD_CONNECTED = 19, 70 | 71 | CLOSING_TIME = 10, 72 | CONNECT_TIME = 30, 73 | UDP_CLOSING_TIME = 120, 74 | 75 | MTU = 1500, 76 | BLOCK_SIZE = 2*MTU, 77 | 78 | NETCMD_SEND_ALLOW = 1, 79 | 80 | MAX_RX_ON_LIMITS = 8192, 81 | MAX_NAME_LEN = 39, 82 | FDID_MAX = 65535 // id field of net cmds encoded as %04hx, so socket fds must not be larger 83 | }; 84 | 85 | struct state { 86 | time_t time{0}; 87 | int fd{-1}; 88 | int state{STATE_INVALID}; 89 | std::string obuf{""}, rnode{""}; 90 | 91 | // deque of { UDP id, data } of UDP datagrams in out queue 92 | std::deque> odgrams; 93 | }; 94 | 95 | } 96 | 97 | namespace config { 98 | 99 | extern std::map tcp_listens, udp_listens, bcmd_tcp_listens; 100 | 101 | extern int socks5_port, socks5_fd, socks4_port, socks4_fd, script_sock; 102 | 103 | extern bool socks5_dns; 104 | 105 | extern std::string local_proxy_ip; 106 | 107 | extern size_t rate_limit_bytes; 108 | 109 | } 110 | 111 | #endif 112 | 113 | 114 | -------------------------------------------------------------------------------- /src/net.cc: -------------------------------------------------------------------------------- 1 | /* 2 | * This file is part of port shell crypter (psc). 3 | * 4 | * (C) 2020-2022 by Sebastian Krahmer, 5 | * sebastian [dot] krahmer [at] gmail [dot] com 6 | * 7 | * psc is free software: you can redistribute it and/or modify 8 | * it under the terms of the GNU General Public License as published by 9 | * the Free Software Foundation, either version 3 of the License, or 10 | * (at your option) any later version. 11 | * 12 | * psc is distributed in the hope that it will be useful, 13 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 14 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 | * GNU General Public License for more details. 16 | * 17 | * You should have received a copy of the GNU General Public License 18 | * along with psc. If not, see . 19 | */ 20 | 21 | #include 22 | #include 23 | #include 24 | #include 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 "misc.h" 38 | #include "net.h" 39 | 40 | 41 | using namespace std; 42 | 43 | namespace ns_psc { 44 | 45 | 46 | // maps "IP/port/ID/" string to actual socket, so that we know 47 | // which socket the tagged cmd data belongs to, which carries IP/port pair in front 48 | map tcp_nodes2sock, udp_nodes2sock; 49 | 50 | 51 | static int listen(int type, const string &ip, const string &port) 52 | { 53 | int r = 0, sock_fd = -1; 54 | addrinfo hint, *tai = nullptr; 55 | memset(&hint, 0, sizeof(hint)); 56 | hint.ai_socktype = type; 57 | 58 | if ((r = getaddrinfo(ip.c_str(), port.c_str(), &hint, &tai)) < 0) 59 | return -1; 60 | 61 | unique_ptr ai(tai, freeaddrinfo); 62 | 63 | if ((sock_fd = socket(ai->ai_family, type, 0)) < 0) 64 | return -1; 65 | 66 | int flags = fcntl(sock_fd, F_GETFL); 67 | fcntl(sock_fd, F_SETFL, flags|O_NONBLOCK); 68 | 69 | int one = 1; 70 | #ifdef SO_REUSEPORT 71 | setsockopt(sock_fd, SOL_SOCKET, SO_REUSEPORT, &one, sizeof(one)); 72 | one = 1; 73 | #endif 74 | setsockopt(sock_fd, SOL_SOCKET, SO_REUSEADDR, &one, sizeof(one)); 75 | 76 | if (::bind(sock_fd, ai->ai_addr, ai->ai_addrlen) < 0) 77 | return -1; 78 | if (type == SOCK_STREAM) { 79 | if (::listen(sock_fd, 12) < 0) 80 | return -1; 81 | } 82 | 83 | return sock_fd; 84 | } 85 | 86 | 87 | int udp_listen(const string &ip, const string &port) 88 | { 89 | return listen(SOCK_DGRAM, ip, port); 90 | } 91 | 92 | 93 | int tcp_listen(const string &ip, const string &port) 94 | { 95 | return listen(SOCK_STREAM, ip, port); 96 | } 97 | 98 | 99 | int unix_listen(const string &path) 100 | { 101 | sockaddr_un sun; 102 | if (path.size() >= sizeof(sun.sun_path)) 103 | return -1; 104 | 105 | unlink(path.c_str()); 106 | 107 | int sfd = socket(PF_UNIX, SOCK_STREAM, 0); 108 | if (sfd < 0) 109 | return -1; 110 | 111 | 112 | memset(&sun, 0, sizeof(sun)); 113 | sun.sun_family = AF_UNIX; 114 | snprintf(sun.sun_path, sizeof(sun.sun_path) - 1, "%s", path.c_str()); 115 | 116 | mode_t um = umask(077); 117 | if (::bind(sfd, reinterpret_cast(&sun), sizeof(sun)) < 0) { 118 | umask(um); 119 | close(sfd); 120 | return -1; 121 | } 122 | if (::listen(sfd, 1) < 0) { 123 | umask(um); 124 | close(sfd); 125 | return -1; 126 | } 127 | umask(um); 128 | return sfd; 129 | } 130 | 131 | 132 | static map resolv_cache; 133 | 134 | 135 | static int connect(int type, const string &name, const string &port) 136 | { 137 | 138 | // if cache has grown largely, drop it and make new 139 | if (resolv_cache.size() > 1024) { 140 | for (const auto &it : resolv_cache) 141 | freeaddrinfo(it.second); 142 | resolv_cache.clear(); 143 | } 144 | 145 | int r = 0, sock_fd = -1, one = 1; 146 | socklen_t len = sizeof(one); 147 | 148 | addrinfo hint, *tai = nullptr; 149 | memset(&hint, 0, sizeof(hint)); 150 | hint.ai_socktype = type; 151 | hint.ai_flags = AI_NUMERICHOST|AI_NUMERICSERV; 152 | 153 | bool can_free = 1; 154 | 155 | if ((r = getaddrinfo(name.c_str(), port.c_str(), &hint, &tai)) != 0) { 156 | 157 | can_free = 0; 158 | 159 | string key = name + ":" + port; 160 | 161 | auto it = resolv_cache.find(key); 162 | 163 | if (it == resolv_cache.end()) { 164 | 165 | hint.ai_flags = AI_NUMERICSERV; 166 | if ((r = getaddrinfo(name.c_str(), port.c_str(), &hint, &tai)) != 0) 167 | return -1; 168 | 169 | resolv_cache[key] = tai; 170 | 171 | } else 172 | tai = it->second; 173 | } 174 | 175 | if ((sock_fd = socket(tai->ai_family, type, 0)) < 0) { 176 | if (can_free) 177 | freeaddrinfo(tai); 178 | return -1; 179 | } 180 | 181 | if (type == SOCK_STREAM) 182 | setsockopt(sock_fd, IPPROTO_TCP, TCP_NODELAY, &one, len); 183 | 184 | int flags = fcntl(sock_fd, F_GETFL); 185 | fcntl(sock_fd, F_SETFL, flags|O_NONBLOCK); 186 | 187 | if (::connect(sock_fd, tai->ai_addr, tai->ai_addrlen) < 0 && errno != EINPROGRESS) { 188 | close(sock_fd); 189 | if (can_free) 190 | freeaddrinfo(tai); 191 | return -1; 192 | } 193 | 194 | return sock_fd; 195 | } 196 | 197 | 198 | 199 | static int udp_connect(const string &ip, const string &port) 200 | { 201 | return connect(SOCK_DGRAM, ip, port); 202 | } 203 | 204 | 205 | static int tcp_connect(const string &ip, const string &port) 206 | { 207 | return connect(SOCK_STREAM, ip, port); 208 | } 209 | 210 | 211 | int maybe_set_rcvbuf(int sock, size_t limit_bytes) 212 | { 213 | // lower TCP rcv buffer size if we don't have large bandwidth to 214 | // remote peer anyways 215 | if (limit_bytes) { 216 | size_t rmem = limit_bytes/10; 217 | socklen_t rmem_len = sizeof(rmem); 218 | if (rmem > 8192) 219 | rmem = 8192; 220 | if (rmem < 4096) 221 | rmem = 4096; 222 | #ifdef SOL_TCP 223 | if (setsockopt(sock, SOL_TCP, SO_RCVBUF, &rmem, rmem_len) < 0) 224 | #else 225 | if (setsockopt(sock, IPPROTO_TCP, SO_RCVBUF, &rmem, rmem_len) < 0) 226 | #endif 227 | return -1; 228 | } 229 | return 0; 230 | } 231 | 232 | 233 | /* 234 | * C:T:N:IP/port/ID/ -> open new TCP connection to IP:port 235 | * C:T:C:IP/port/ID/ -> connection to IP:port is estabished on remote side 236 | * C:T:S:IP/port/ID/data -> send data to IP:port 237 | * C:T:R:IP/port/ID/data -> data received from IP:port on remote side 238 | * C:T:F:IP/port/ID/ -> close connection belonging to IP:port 239 | * 240 | * C:U:S:IP/port/ID/ -> send UDP datagram to IP:port 241 | * C:U:R:IP/port/ID/ -> received UDP datagram from IP:port on remote side 242 | * 243 | */ 244 | 245 | int cmd_handler(const string &cmd, state *fd2state, pollfd *pfds, uint32_t flags) 246 | { 247 | char C[16] = {0}, proto[16] = {0}, op[16] = {0}, host[128] = {0}; 248 | uint16_t port = 0, id = 0; 249 | int sock = -1; 250 | 251 | // ID is the logical channel to distinguish between multiple same host:port connections. 252 | // The accepted socket fd of the local psc part is unique and good for it. 253 | if (sscanf(cmd.c_str(), "%15[^:]:%15[^:]:%15[^:]:%127[^/]/%04hx/%04hx/", C, proto, op, host, &port, &id) != 6) 254 | return -1; 255 | 256 | auto slash = cmd.find("/"); 257 | const string node = string(host) + cmd.substr(slash, 11); 258 | 259 | if (C[0] != 'C' || (proto[0] != 'T' && proto[0] != 'U')) 260 | return -1; 261 | 262 | // open new non-blocking connection 263 | if (cmd.find("C:T:N:") == 0 && (flags & NETCMD_SEND_ALLOW)) { 264 | if ((sock = tcp_connect(host, to_string(port))) < 0) 265 | return -1; 266 | 267 | pfds[sock].revents = 0; 268 | pfds[sock].events = POLLOUT; 269 | pfds[sock].fd = sock; 270 | 271 | fd2state[sock].fd = sock; 272 | fd2state[sock].state = STATE_CONNECT; 273 | fd2state[sock].obuf.clear(); 274 | fd2state[sock].odgrams.clear(); 275 | fd2state[sock].rnode = node; 276 | fd2state[sock].time = time(nullptr); 277 | 278 | tcp_nodes2sock[node] = sock; 279 | 280 | // non-blocking connect() got ready 281 | } else if (cmd.find("C:T:C:") == 0) { 282 | auto it = tcp_nodes2sock.find(node); 283 | if (it == tcp_nodes2sock.end()) 284 | return -1; 285 | sock = it->second; 286 | 287 | pfds[sock].events = POLLIN; 288 | 289 | fd2state[sock].fd = sock; 290 | fd2state[sock].state = STATE_CONNECTED; 291 | fd2state[sock].obuf.clear(); 292 | fd2state[sock].odgrams.clear(); 293 | fd2state[sock].time = time(nullptr); 294 | 295 | // finish connection 296 | } else if (cmd.find("C:T:F:") == 0) { 297 | auto it = tcp_nodes2sock.find(node); 298 | if (it == tcp_nodes2sock.end()) 299 | return -1; 300 | sock = it->second; 301 | tcp_nodes2sock.erase(it); 302 | 303 | // flush remaining data 304 | if (fd2state[sock].obuf.size() > 0) 305 | writen(sock, fd2state[sock].obuf.c_str(), fd2state[sock].obuf.size()); 306 | 307 | // sock will be closed in main poll() loop via timeout 308 | shutdown(sock, SHUT_RDWR); 309 | pfds[sock].fd = -1; 310 | pfds[sock].events = 0; 311 | 312 | fd2state[sock].state = STATE_CLOSING; 313 | fd2state[sock].obuf.clear(); 314 | fd2state[sock].odgrams.clear(); 315 | fd2state[sock].time = time(nullptr); 316 | 317 | // Send or receive data. No NETCMD_SEND_ALLOW check, since the node will not be in 318 | // the tcp_nodes2sock map in the first place, as there was no tcp_connect() and no map 319 | // insertion. 320 | } else if (cmd.find("C:T:S:") == 0 || cmd.find("C:T:R:") == 0) { 321 | auto it = tcp_nodes2sock.find(node); 322 | if (it == tcp_nodes2sock.end()) 323 | return -1; 324 | sock = it->second; 325 | pfds[sock].events |= POLLOUT; 326 | 327 | fd2state[sock].obuf += cmd.substr(6 + node.size()); // strip off data part 328 | fd2state[sock].time = time(nullptr); 329 | 330 | } else if (cmd.find("C:U:S:") == 0 || cmd.find("C:U:R:") == 0) { 331 | auto it = udp_nodes2sock.find(node); 332 | if (it == udp_nodes2sock.end()) { 333 | if (!(flags & NETCMD_SEND_ALLOW)) 334 | return 0; 335 | if ((sock = udp_connect(host, to_string(port))) < 0) 336 | return -1; 337 | udp_nodes2sock[node] = sock; 338 | 339 | // Just fill rnode part in server side. client main loop expects ID/ part not to be 340 | // appended 341 | fd2state[sock].rnode = node; 342 | fd2state[sock].state = STATE_UDPCLIENT; 343 | fd2state[sock].fd = sock; 344 | } else 345 | sock = it->second; 346 | 347 | pfds[sock].revents = 0; 348 | pfds[sock].fd = sock; 349 | pfds[sock].events = POLLIN; 350 | 351 | if (cmd.size() > 6 + node.size()) { 352 | fd2state[sock].odgrams.push_back({id, cmd.substr(6 + node.size())}); // strip off data part 353 | pfds[sock].events |= POLLOUT; 354 | } 355 | fd2state[sock].time = time(nullptr); 356 | } 357 | 358 | return 0; 359 | } 360 | 361 | } 362 | 363 | -------------------------------------------------------------------------------- /src/net.h: -------------------------------------------------------------------------------- 1 | /* 2 | * This file is part of port shell crypter (psc). 3 | * 4 | * (C) 2006-2022 by Sebastian Krahmer, 5 | * sebastian [dot] krahmer [at] gmail [dot] com 6 | * 7 | * psc is free software: you can redistribute it and/or modify 8 | * it under the terms of the GNU General Public License as published by 9 | * the Free Software Foundation, either version 3 of the License, or 10 | * (at your option) any later version. 11 | * 12 | * psc is distributed in the hope that it will be useful, 13 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 14 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 | * GNU General Public License for more details. 16 | * 17 | * You should have received a copy of the GNU General Public License 18 | * along with psc. If not, see . 19 | */ 20 | 21 | #ifndef psc_net_h 22 | #define psc_net_h 23 | 24 | #include 25 | #include 26 | #include 27 | #include 28 | #include 29 | #include 30 | #include 31 | 32 | #include "misc.h" 33 | 34 | 35 | namespace ns_psc { 36 | 37 | 38 | extern std::map tcp_nodes2sock, udp_nodes2sock; 39 | 40 | int tcp_listen(const std::string &, const std::string &); 41 | 42 | int udp_listen(const std::string &, const std::string &); 43 | 44 | int unix_listen(const std::string &); 45 | 46 | int maybe_set_rcvbuf(int, size_t); 47 | 48 | int cmd_handler(const std::string &, state *, pollfd *, uint32_t flags = 0); 49 | 50 | 51 | struct alignas(4) v4_tuple { 52 | in_addr dst; 53 | uint16_t dport; 54 | }; 55 | 56 | 57 | struct alignas(4) v6_tuple { 58 | in6_addr dst; 59 | uint16_t dport; 60 | }; 61 | 62 | 63 | struct alignas(1) name_tuple { 64 | uint8_t nlen; 65 | char name[255 + 2]; // +2 for dst port 66 | }; 67 | 68 | struct socks5_req { 69 | uint8_t vers, cmd, mbz, atype; 70 | union alignas(4) { 71 | v4_tuple v4; 72 | v6_tuple v6; 73 | name_tuple name; 74 | }; 75 | }; // no __attribute__((packed)) needed, as its properly aligned 76 | 77 | 78 | struct socks4_req { 79 | uint8_t ver, cmd; 80 | uint16_t dport; 81 | uint32_t dst; 82 | uint8_t id; 83 | }; 84 | 85 | 86 | // TCP connections can use the socket fd as id, as it uniquely identifies the connection. 87 | // UDP sockets receive all datagrams on the same fd, so we cannot use the fd as an connection-id and therefore 88 | // need to map the dgram's originating struct sockaddr {} (implemented as string blob) to an unqiue id, 89 | // so that we know where to send replies to when we receive data for that id. 90 | class udp_node2id { 91 | 92 | std::map d_id2node; 93 | std::map d_node2id; 94 | 95 | enum { max_id = 0xffff }; 96 | 97 | uint16_t d_next_id{0}; 98 | 99 | public: 100 | 101 | uint16_t put(const std::string &addr) 102 | { 103 | // if origin already exists, take this ID 104 | auto it = d_node2id.find(addr); 105 | if (it != d_node2id.end()) 106 | return it->second; 107 | 108 | // if there are free IDs, pick a new one 109 | if (d_node2id.size() <= max_id) { 110 | d_node2id[addr] = d_next_id; 111 | d_id2node[d_next_id] = addr; 112 | return d_next_id++; 113 | } 114 | 115 | // otherwise flush all mappings (possibly corrupt outstanding UDP sessions) 116 | d_node2id.clear(); 117 | d_id2node.clear(); 118 | d_next_id = 0; 119 | 120 | d_node2id[addr] = d_next_id; 121 | d_id2node[d_next_id] = addr; 122 | return d_next_id++; 123 | } 124 | 125 | std::string get(uint16_t id) 126 | { 127 | std::string ret = ""; 128 | auto it = d_id2node.find(id); 129 | if (it != d_id2node.end()) 130 | ret = it->second; 131 | 132 | return ret; 133 | } 134 | 135 | void del(uint16_t id) 136 | { 137 | auto it = d_id2node.find(id); 138 | if (it != d_id2node.end()) { 139 | auto it2 = d_node2id.find(it->second); // must exist 140 | d_node2id.erase(it2); 141 | d_id2node.erase(it); 142 | } 143 | } 144 | }; 145 | 146 | 147 | extern std::map tcp_nodes2sock, udp_nodes2sock; 148 | 149 | } 150 | 151 | #endif 152 | 153 | -------------------------------------------------------------------------------- /src/pcwrap.cc: -------------------------------------------------------------------------------- 1 | /* 2 | * This file is part of port shell crypter (psc). 3 | * 4 | * (C) 2006-2023 by Sebastian Krahmer, 5 | * sebastian [dot] krahmer [at] gmail [dot] com 6 | * 7 | * psc is free software: you can redistribute it and/or modify 8 | * it under the terms of the GNU General Public License as published by 9 | * the Free Software Foundation, either version 3 of the License, or 10 | * (at your option) any later version. 11 | * 12 | * psc is distributed in the hope that it will be useful, 13 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 14 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 | * GNU General Public License for more details. 16 | * 17 | * You should have received a copy of the GNU General Public License 18 | * along with psc. If not, see . 19 | */ 20 | 21 | #include 22 | #include 23 | #include 24 | #include 25 | #include 26 | #include 27 | #include 28 | #include 29 | #include 30 | #include 31 | #include 32 | #include 33 | 34 | #include "pcwrap.h" 35 | #include "misc.h" 36 | 37 | #include "external/aes.h" 38 | #include "external/sha512.h" 39 | 40 | 41 | using namespace std; 42 | 43 | namespace ns_psc { 44 | 45 | 46 | const string PSC_STARTTLS = START_BANNER; 47 | 48 | 49 | pc_wrap::pc_wrap(int rfd, int wfd) 50 | : d_r_fd(rfd), d_w_fd(wfd) 51 | { 52 | d_inq.reserve(16*BLOCK_SIZE); 53 | d_recent.reserve(4096); 54 | } 55 | 56 | 57 | static int kdf(const char *secret, int slen, unsigned char key[SHA512_SIZE]) 58 | { 59 | if (slen <= 0) 60 | return -1; 61 | memset(key, 0xff, SHA512_SIZE); 62 | 63 | sha512_context md_ctx; 64 | 65 | sha512_init(&md_ctx); 66 | sha512_update(&md_ctx, reinterpret_cast(secret), slen); 67 | 68 | string vs = "v1"; 69 | sha512_update(&md_ctx, reinterpret_cast(vs.c_str()), vs.size()); 70 | uint8_t *md = sha512_final(&md_ctx); 71 | 72 | memcpy(key, md, SHA512_SIZE); 73 | return 0; 74 | } 75 | 76 | 77 | int pc_wrap::init(const string &k1, const string &k2, bool s) 78 | { 79 | d_server_mode = s; 80 | 81 | d_err = "pc_wrap::init: Initializing crypto CTX failed."; 82 | 83 | unsigned char tmp[16] = {0}; 84 | if (RAND_bytes(tmp, sizeof(tmp)) != 1) 85 | return -1; 86 | 87 | b64_encode(reinterpret_cast(tmp), sizeof(tmp), d_iv); 88 | memset(d_iv + 16, 0, sizeof(d_iv) - 16); 89 | 90 | if (kdf(k1.c_str(), k1.size(), d_w_key) < 0) 91 | return -1; 92 | if (kdf(k2.c_str(), k2.size(), d_r_key) < 0) 93 | return -1; 94 | 95 | d_err = ""; 96 | 97 | AES_init_ctx(&d_w_ctx, d_w_key); 98 | AES_init_ctx(&d_r_ctx, d_r_key); 99 | 100 | return 0; 101 | } 102 | 103 | 104 | int pc_wrap::reset() 105 | { 106 | d_seen_starttls = 0; 107 | 108 | if (!d_server_mode) 109 | tcsetattr(d_r_fd, TCSANOW, &d_saved_rfd_tattr); 110 | 111 | d_err = "pc_wrap::reset: resetting crypto CTX failed."; 112 | 113 | unsigned char tmp[16] = {0}; 114 | if (RAND_bytes(tmp, sizeof(tmp)) != 1) 115 | return -1; 116 | 117 | b64_encode(reinterpret_cast(tmp), sizeof(tmp), d_iv); 118 | 119 | d_err = ""; 120 | 121 | AES_init_ctx(&d_w_ctx, d_w_key); 122 | AES_init_ctx(&d_r_ctx, d_r_key); 123 | 124 | return 0; 125 | } 126 | 127 | 128 | pc_wrap::~pc_wrap() 129 | { 130 | } 131 | 132 | 133 | int pc_wrap::enable_crypto() 134 | { 135 | AES_ctx_set_iv(&d_w_ctx, reinterpret_cast(d_iv)); 136 | AES_ctx_set_iv(&d_r_ctx, reinterpret_cast(d_iv)); 137 | 138 | d_seen_starttls = 1; 139 | 140 | return 0; 141 | } 142 | 143 | 144 | int pc_wrap::check_wsize(int fd) 145 | { 146 | if (!d_wsize_signalled) 147 | return 0; 148 | d_wsize_signalled = 0; 149 | int r = ioctl(fd, TIOCSWINSZ, &d_ws); 150 | if (r == 0) 151 | return 1; 152 | return r; 153 | } 154 | 155 | 156 | string pc_wrap::decrypt(const string &buf) 157 | { 158 | string result = ""; 159 | if (buf.size() <= 0 || buf.size() > 2*BLOCK_SIZE) 160 | return result; 161 | 162 | char obuf[2*BLOCK_SIZE] = {0}; 163 | 164 | AES_CTR_xcrypt(&d_r_ctx, reinterpret_cast(buf.c_str()), buf.size(), reinterpret_cast(obuf)); 165 | 166 | result.assign(obuf, buf.size()); 167 | return result; 168 | } 169 | 170 | 171 | string pc_wrap::encrypt(const string &buf) 172 | { 173 | string result = ""; 174 | if (buf.size() <= 0 || buf.size() > 2*BLOCK_SIZE) 175 | return result; 176 | 177 | char obuf[2*BLOCK_SIZE] = {0}; 178 | 179 | AES_CTR_xcrypt(&d_w_ctx, reinterpret_cast(buf.c_str()), buf.size(), reinterpret_cast(obuf)); 180 | 181 | result.assign(obuf, buf.size()); 182 | return result; 183 | } 184 | 185 | 186 | int pc_wrap::read(bool nosys, string &buf, string &ext_cmd, int &starttls) 187 | { 188 | ssize_t r; 189 | char tbuf[2*BLOCK_SIZE] = {0}; // do not change buf-size w/o reflecting ::encrypt() and ::decrypt() limits 190 | string inbuf = ""; 191 | 192 | buf.clear(); 193 | ext_cmd.clear(); 194 | starttls = 0; 195 | 196 | string::size_type idx1 = 0, idx2 = 0; 197 | 198 | // Do not call syscall if we just want to check for remaining data to avoid 199 | // blocking other pollfd's in the main loop by calling read() on the same fd 200 | // again and again for potentially mass of data pumped through 201 | if (!nosys) { 202 | if ((r = ::read(d_r_fd, tbuf, sizeof(tbuf))) <= 0) { 203 | d_err = "pc_wrap::read::"; 204 | d_err += strerror(errno); 205 | return -1; 206 | } 207 | inbuf = string(tbuf, r); 208 | d_inq += inbuf; 209 | } 210 | 211 | if (d_seen_starttls) { 212 | 213 | if ((idx2 = d_inq.find(")")) == string::npos) 214 | return 0; 215 | if ((idx1 = d_inq.find("(")) == string::npos) { 216 | d_inq.clear(); 217 | return 0; 218 | } 219 | 220 | // silently ignore too large chunks 221 | if (idx2 - idx1 > BLOCK_SIZE || idx2 < idx1) { 222 | d_inq.clear(); 223 | return 0; 224 | } 225 | 226 | // when here, we have a valid b64 encoded crypted string. b64 decode will automatically 227 | // stop at closing `)` since its an invalid B64 char and so the target bufsize is large enough 228 | r = b64_decode(d_inq.c_str() + idx1 + 1, reinterpret_cast(tbuf)); 229 | string s = decrypt(string(tbuf, r)); 230 | 231 | d_inq.erase(0, idx2 + 1); 232 | 233 | // normal data? 234 | if (s.find("D:0:") == 0) { 235 | buf = s.substr(4); 236 | // window-size command 237 | } else if (s.find("C:WS:") == 0) { 238 | d_wsize_signalled = 1; 239 | if (sscanf(s.c_str() + 5, "%hu:%hu:%hu:%hu", &d_ws.ws_row, &d_ws.ws_col, 240 | &d_ws.ws_xpixel, &d_ws.ws_ypixel) != 4) 241 | d_wsize_signalled = 0; 242 | } else if (s.find("C:exit:") == 0) { 243 | // if pscr is executed directly in pscl, there is a race of pscr vanishing with its 244 | // pty while we are trying to reset pty master to old state. Increase chances of pscr 245 | // finishing and pscl (us) resetting the right pty and not the pty of exiting pscr. 246 | usleep(50000); 247 | 248 | // psc-remote is quitting, reset crypto state 249 | if (this->reset() < 0) 250 | printf("\r\npscl: Seen end-sequence, but resetting crypto state failed! Continuing halfdead.\r\n"); 251 | else 252 | printf("\r\npscl: Seen end-sequence, disabling crypto!\r\npscl: If tty is hangup, type 'reset'.\r\n"); 253 | 254 | // any other command needs to be handled by external filter 255 | } else if (s.find("C:") == 0) 256 | ext_cmd = move(s); 257 | 258 | // more complete data blobs in the in queue? 259 | return d_inq.find(")") != string::npos; 260 | } 261 | 262 | d_recent += inbuf; 263 | 264 | // as slow links read output one-bye-one or in small chunks, we need 265 | // to slide-match STARTTLS sequence 266 | if (d_recent.size() >= PSC_STARTTLS.size() + 16 && (idx1 = d_recent.find(PSC_STARTTLS)) != string::npos) { 267 | memcpy(d_iv, d_recent.c_str() + idx1 + PSC_STARTTLS.size(), 16); 268 | 269 | d_recent.erase(0, idx1 + PSC_STARTTLS.size() + 16); 270 | 271 | AES_ctx_set_iv(&d_w_ctx, reinterpret_cast(d_iv)); 272 | AES_ctx_set_iv(&d_r_ctx, reinterpret_cast(d_iv)); 273 | 274 | printf("\r\npscl: Seen STARTTLS sequence, enabling crypto.\r\n"); 275 | d_seen_starttls = 1; 276 | starttls = 1; 277 | if (!d_server_mode) { 278 | // Disable local echo now, since remote site is 279 | // opening another PTY with echo 280 | struct termios tattr; 281 | if (tcgetattr(d_r_fd, &tattr) == 0) { 282 | d_saved_rfd_tattr = tattr; 283 | cfmakeraw(&tattr); 284 | tattr.c_cc[VMIN] = 1; 285 | tattr.c_cc[VTIME] = 0; 286 | tcsetattr(d_r_fd, TCSANOW, &tattr); 287 | } 288 | // window size will be signalled in main loop() since we set starttls = 1 289 | } 290 | 291 | buf = d_inq; 292 | 293 | // the remaining bytes after STARTTLS tag 294 | d_inq = d_recent; 295 | 296 | // Everything after starttls sequence will be b64encrypted, so check whether there 297 | // is already an entire block read 298 | return d_inq.find(")") != string::npos; 299 | } 300 | 301 | string::size_type nl = d_recent.find_last_of('\n'); 302 | if (nl != string::npos && nl + 1 < d_recent.size()) 303 | d_recent.erase(0, nl + 1); 304 | 305 | buf = d_inq; 306 | d_inq.clear(); 307 | return 0; 308 | } 309 | 310 | 311 | string pc_wrap::possibly_b64encrypt(const std::string &tag, const string &buf) 312 | { 313 | string r = ""; 314 | 315 | if (buf.size() > BLOCK_SIZE) { 316 | d_err = "pc_wrap::possibly_b64encrypt: bufsize too large"; 317 | return r; 318 | } 319 | 320 | unsigned char *b64_crypt_buf = nullptr; 321 | 322 | if (d_seen_starttls) { 323 | string s = encrypt(tag + buf); 324 | b64_crypt_buf = new (nothrow) unsigned char[2*s.size()]; 325 | if (!b64_crypt_buf) 326 | return r; 327 | memset(b64_crypt_buf, 0, 2*s.size()); 328 | r = "("; 329 | r += b64_encode(s.c_str(), s.size(), b64_crypt_buf); 330 | r += ")"; 331 | 332 | delete [] b64_crypt_buf; 333 | return r; 334 | } 335 | 336 | // if starttls not seen yet, just pass plain buf 337 | r = buf; 338 | return r; 339 | } 340 | 341 | 342 | string pc_wrap::wsize_cmd() 343 | { 344 | if (!d_seen_starttls) 345 | return ""; 346 | 347 | char wsbuf[64] = {0}; 348 | if (ioctl(0, TIOCGWINSZ, &d_ws) < 0) 349 | return ""; 350 | snprintf(wsbuf, sizeof(wsbuf), "WS:%hu:%hu:%hu:%hu", d_ws.ws_row, 351 | d_ws.ws_col, d_ws.ws_xpixel, d_ws.ws_ypixel); 352 | return possibly_b64encrypt("C:", wsbuf); 353 | } 354 | 355 | 356 | int pc_wrap::r_fileno() 357 | { 358 | return d_r_fd; 359 | } 360 | 361 | 362 | int pc_wrap::w_fileno() 363 | { 364 | return d_w_fd; 365 | } 366 | 367 | 368 | const char *pc_wrap::why() 369 | { 370 | return d_err.c_str(); 371 | } 372 | 373 | } 374 | 375 | -------------------------------------------------------------------------------- /src/pcwrap.h: -------------------------------------------------------------------------------- 1 | /* 2 | * This file is part of port shell crypter (psc). 3 | * 4 | * (C) 2006-2020 by Sebastian Krahmer, 5 | * sebastian [dot] krahmer [at] gmail [dot] com 6 | * 7 | * psc is free software: you can redistribute it and/or modify 8 | * it under the terms of the GNU General Public License as published by 9 | * the Free Software Foundation, either version 3 of the License, or 10 | * (at your option) any later version. 11 | * 12 | * psc is distributed in the hope that it will be useful, 13 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 14 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 | * GNU General Public License for more details. 16 | * 17 | * You should have received a copy of the GNU General Public License 18 | * along with psc. If not, see . 19 | */ 20 | 21 | /* Plain/crypted forwrad wrapper */ 22 | 23 | #ifndef psc_pcwrap_h 24 | #define psc_pcwrap_h 25 | 26 | #include 27 | #include 28 | #include 29 | #include 30 | #include 31 | #include 32 | #include "external/aes.h" 33 | 34 | namespace ns_psc { 35 | 36 | class pc_wrap { 37 | private: 38 | 39 | int d_r_fd{-1}, d_w_fd{-1}; 40 | bool d_seen_starttls{0}; 41 | std::string d_err{""}, d_recent{""}, d_inq{""}; 42 | bool d_server_mode{0}; 43 | struct winsize d_ws; 44 | bool d_wsize_signalled{0}; 45 | 46 | termios d_saved_rfd_tattr; 47 | AES_ctx d_r_ctx, d_w_ctx; 48 | unsigned char d_w_key[64]{0}, d_r_key[64]{0}; 49 | unsigned char d_iv[32]{0}; 50 | 51 | std::string encrypt(const std::string &); 52 | 53 | std::string decrypt(const std::string &); 54 | 55 | public: 56 | pc_wrap(int, int); 57 | 58 | int init(const std::string &, const std::string &, bool); 59 | 60 | int reset(); 61 | 62 | ~pc_wrap(); 63 | 64 | int read(bool, std::string &, std::string &, int &); 65 | 66 | std::string possibly_b64encrypt(const std::string &, const std::string &); 67 | 68 | std::string wsize_cmd(); 69 | 70 | int check_wsize(int); 71 | 72 | int r_fileno(); 73 | 74 | int w_fileno(); 75 | 76 | const char *why(); 77 | 78 | int enable_crypto(); 79 | 80 | bool is_crypted() { return d_seen_starttls; } 81 | 82 | char *get_iv() { return reinterpret_cast(d_iv); } 83 | }; 84 | 85 | } 86 | 87 | #endif 88 | 89 | -------------------------------------------------------------------------------- /src/pscsh.cc: -------------------------------------------------------------------------------- 1 | /* 2 | * This file is part of port shell crypter (psc). 3 | * 4 | * (C) 2006-2022 by Sebastian Krahmer, 5 | * sebastian [dot] krahmer [at] gmail [dot] com 6 | * 7 | * psc is free software: you can redistribute it and/or modify 8 | * it under the terms of the GNU General Public License as published by 9 | * the Free Software Foundation, either version 3 of the License, or 10 | * (at your option) any later version. 11 | * 12 | * psc is distributed in the hope that it will be useful, 13 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 14 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 | * GNU General Public License for more details. 16 | * 17 | * You should have received a copy of the GNU General Public License 18 | * along with psc. If not, see . 19 | */ 20 | 21 | #include 22 | #include 23 | #include 24 | #include 25 | #include 26 | #include 27 | #include 28 | #include 29 | #include 30 | #include 31 | #include 32 | #include 33 | #include 34 | 35 | 36 | using namespace std; 37 | 38 | 39 | void die(const char *msg) 40 | { 41 | perror(msg); 42 | exit(errno); 43 | } 44 | 45 | 46 | int script_loop(const string &script_socket, const string &script_file) 47 | { 48 | 49 | struct sockaddr_un sun; 50 | 51 | if (script_socket.size() >= sizeof(sun.sun_path) - 1) { 52 | errno = -E2BIG; 53 | return -1; 54 | } 55 | 56 | memset(&sun, 0, sizeof(sun)); 57 | sun.sun_family = AF_UNIX; 58 | strcpy(sun.sun_path, script_socket.c_str()); 59 | 60 | int sfd = -1; 61 | if ((sfd = socket(PF_UNIX, SOCK_STREAM, 0)) < 0) 62 | return -1; 63 | 64 | if (connect(sfd, reinterpret_cast(&sun), sizeof(sun)) < 0) 65 | return -1; 66 | 67 | int ifd = 0, n = 0; 68 | char buf[4096] = {0}; 69 | string sbuf = "", obuf = "", end_marker = "###everlong###", end_detect = ""; 70 | 71 | if (script_file.size() > 0) { 72 | if ((ifd = open(script_file.c_str(), O_RDONLY)) < 0) 73 | return -1; 74 | } 75 | 76 | pollfd pfd[3] = {{ifd, POLLIN, 0}, {1, 0, 0}, {sfd, POLLIN, 0}}; 77 | 78 | for (bool leave = 0, end_detected = 0; !leave;) { 79 | 80 | if ((n = poll(pfd, 3, -1)) < 0) 81 | break; 82 | if (n == 0) 83 | continue; 84 | 85 | for (unsigned int i = 0; i < sizeof(pfd)/sizeof(pfd[0]); ++i) { 86 | 87 | if (pfd[i].revents & POLLIN) { 88 | 89 | pfd[i].revents = 0; 90 | 91 | if ((n = read(pfd[i].fd, buf, sizeof(buf))) <= 0) { 92 | if (pfd[i].fd == ifd) { // End of script? No more reads. 93 | sbuf += end_marker + "\n"; // add end-marker to stream and wait for it to appear on remote pty-echo to notice finishing of script 94 | pfd[2].events |= POLLOUT; 95 | pfd[i].events = 0; 96 | } else 97 | leave = 1; 98 | } 99 | if (n > 0 && pfd[i].fd == ifd) { // read from stdin/script 100 | sbuf += string(buf, n); 101 | pfd[2].events |= POLLOUT; // write to socket 102 | } else if (n > 0 && pfd[i].fd == sfd) { // read from socket 103 | obuf += string(buf, n); 104 | pfd[1].events |= POLLOUT; // write to stdout 105 | 106 | end_detect += string(buf, n); 107 | if (end_detect.find(end_marker) != string::npos) { 108 | pfd[i].events = 0; 109 | end_detected = 1; 110 | } 111 | if (end_detect.size() > 3*end_marker.size()) 112 | end_detect.erase(0, end_marker.size()); 113 | } 114 | } 115 | if (pfd[i].revents & POLLOUT) { 116 | 117 | pfd[i].revents = 0; 118 | 119 | if (pfd[i].fd == 1) { 120 | if ((n = write(1, obuf.c_str(), obuf.size())) <= 0) { 121 | leave = 1; 122 | break; 123 | } 124 | obuf.erase(0, n); 125 | if (obuf.size() == 0) { 126 | pfd[i].events &= ~POLLOUT; 127 | if (end_detected) 128 | leave = 1; 129 | } 130 | } else if (pfd[i].fd == sfd) { 131 | if ((n = write(sfd, sbuf.c_str(), sbuf.size())) <= 0) { 132 | leave = 1; 133 | break; 134 | } 135 | sbuf.erase(0, n); 136 | if (sbuf.size() == 0) 137 | pfd[i].events &= ~POLLOUT; 138 | } 139 | } 140 | } 141 | } 142 | 143 | close(sfd); 144 | if (script_file.size() > 0) 145 | close(ifd); 146 | 147 | return 0; 148 | } 149 | 150 | 151 | void usage(const char *argv0) 152 | { 153 | printf("\nUsage: %s [-S script socket] [-f script file]\n\n", argv0); 154 | } 155 | 156 | 157 | int main(int argc, char **argv) 158 | { 159 | int c = -1; 160 | string script_socket = "", script_file = ""; 161 | 162 | if (getenv("HOME")) 163 | script_socket = string(getenv("HOME")) + "/psc.script_sock"; 164 | 165 | while ((c = getopt(argc, argv, "f:S:h")) != -1) { 166 | switch (c) { 167 | case 'S': 168 | script_socket = optarg; 169 | break; 170 | case 'f': 171 | if (strncmp(optarg, "script_", 7)) { 172 | fprintf(stderr, "Script file must start with 'script_'.\n"); 173 | exit(1); 174 | } else 175 | script_file = optarg; 176 | break; 177 | case 'h': 178 | default: 179 | usage(argv[0]); 180 | exit(0); 181 | } 182 | } 183 | 184 | if (script_loop(script_socket, script_file) < 0) 185 | die("script_loop"); 186 | 187 | return 0; 188 | } 189 | 190 | -------------------------------------------------------------------------------- /src/pty.cc: -------------------------------------------------------------------------------- 1 | /* 2 | * This file is part of port shell crypter (psc). 3 | * 4 | * (C) 2006-2021 by Sebastian Krahmer, 5 | * sebastian [dot] krahmer [at] gmail [dot] com 6 | * 7 | * psc is free software: you can redistribute it and/or modify 8 | * it under the terms of the GNU General Public License as published by 9 | * the Free Software Foundation, either version 3 of the License, or 10 | * (at your option) any later version. 11 | * 12 | * psc is distributed in the hope that it will be useful, 13 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 14 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 | * GNU General Public License for more details. 16 | * 17 | * You should have received a copy of the GNU General Public License 18 | * along with psc. If not, see . 19 | */ 20 | 21 | #include "pty.h" 22 | #include 23 | #include 24 | #include 25 | #include 26 | #include 27 | #include 28 | #include 29 | #include 30 | #include 31 | 32 | using namespace std; 33 | 34 | namespace ns_psc { 35 | 36 | pty::pty(const pty &rhs) 37 | { 38 | if (this == &rhs) 39 | return; 40 | _slave = dup(rhs._slave); _master = dup(rhs._master); 41 | m = rhs.m; s = rhs.s; 42 | } 43 | 44 | pty &pty::operator=(const pty &rhs) 45 | { 46 | if (this == &rhs) 47 | return *this; 48 | _slave = dup(rhs._slave); _master = dup(rhs._master); 49 | m = rhs.m; s = rhs.s; 50 | return *this; 51 | } 52 | 53 | 54 | // Open master+slave terminal 55 | // returns 0 on sucess, -1 on failure 56 | int pty::open() 57 | { 58 | #ifdef OLD_BSD_PTY 59 | char ptys[] = "/dev/ptyXY"; 60 | const char *it1 = "pqrstuvwxyzPQRST", 61 | *it2 = "0123456789abcdef"; 62 | 63 | for (; *it1 != 0; ++it1) { 64 | ptys[8] = *it1; 65 | for (; *it2 != 0; ++it2) { 66 | ptys[9] = *it2; 67 | 68 | // do master-part 69 | if ((_master = ::open(ptys, O_RDWR|O_NOCTTY)) < 0) { 70 | if (errno == ENOENT) { 71 | serr = strerror(errno); 72 | return -1; 73 | } else 74 | continue; 75 | } 76 | m = ptys; 77 | ptys[5] = 't'; 78 | 79 | // master finished. Do slave-part. 80 | if ((_slave = ::open(ptys, O_RDWR|O_NOCTTY)) < 0) { 81 | ::close(_master); 82 | serr = strerror(errno); 83 | return -1; 84 | } 85 | s = ptys; 86 | return 0; 87 | } 88 | } 89 | 90 | // out of terminals 91 | serr = "Dance the funky chicken (or use Unix98 pty's)."; 92 | return -1; 93 | 94 | #else 95 | if ((_master = posix_openpt(O_RDWR|O_NOCTTY)) < 0) { 96 | serr = strerror(errno); 97 | return -1; 98 | } 99 | 100 | grantpt(_master); 101 | unlockpt(_master); 102 | s = ptsname(_master); 103 | 104 | // master finished. Do slave-part. 105 | if ((_slave = ::open(s.c_str(), O_RDWR|O_NOCTTY)) < 0) { 106 | ::close(_master); 107 | serr = strerror(errno); 108 | return -1; 109 | } 110 | 111 | fchmod(_slave, 0600); 112 | return 0; 113 | #endif 114 | } 115 | 116 | 117 | int pty::close() 118 | { 119 | ::close(_master); _master = -1; 120 | ::close(_slave); _slave = -1; 121 | return 0; 122 | } 123 | 124 | int pty::grant(uid_t u, gid_t g, mode_t mode) 125 | { 126 | 127 | #ifdef OLD_BSD_PTY 128 | if (chown(m.c_str(), u, g) < 0 || chown(s.c_str(), u, g) < 0) { 129 | serr = strerror(errno); 130 | return -1; 131 | } 132 | mode_t mask = umask(0); 133 | if (chmod(m.c_str(), mode) < 0 || chmod(s.c_str(), mode) < 0) { 134 | serr = strerror(errno); 135 | umask(mask); 136 | return -1; 137 | } 138 | umask(mask); 139 | #endif 140 | 141 | return 0; 142 | } 143 | 144 | const char *pty::why() 145 | { 146 | return serr.c_str(); 147 | } 148 | 149 | } 150 | 151 | -------------------------------------------------------------------------------- /src/pty.h: -------------------------------------------------------------------------------- 1 | /* 2 | * This file is part of port shell crypter (psc). 3 | * 4 | * (C) 2006-2020 by Sebastian Krahmer, 5 | * sebastian [dot] krahmer [at] gmail [dot] com 6 | * 7 | * psc is free software: you can redistribute it and/or modify 8 | * it under the terms of the GNU General Public License as published by 9 | * the Free Software Foundation, either version 3 of the License, or 10 | * (at your option) any later version. 11 | * 12 | * psc is distributed in the hope that it will be useful, 13 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 14 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 | * GNU General Public License for more details. 16 | * 17 | * You should have received a copy of the GNU General Public License 18 | * along with psc. If not, see . 19 | */ 20 | 21 | #ifndef psc_pty_h 22 | #define psc_pty_h 23 | 24 | #include 25 | #include 26 | #include 27 | #include 28 | #include 29 | 30 | 31 | namespace ns_psc { 32 | 33 | // A BSD 4.3+ PTY API. 34 | class pty { 35 | protected: 36 | // file-descriptors for terminal 37 | int _master, _slave; 38 | 39 | // names of device-files 40 | std::string m, s, serr; 41 | public: 42 | pty() : _master(-1), _slave(-1), m(""), s(""), serr("") {} 43 | 44 | 45 | virtual ~pty() { close(); } 46 | 47 | // Copy-constructor 48 | pty(const pty &rhs); 49 | 50 | // Assign-operator 51 | pty &operator=(const pty &rhs); 52 | 53 | // open master+slave terminal 54 | virtual int open(); 55 | 56 | // close both 57 | int close(); 58 | 59 | int master() { return _master; } 60 | 61 | int slave() { return _slave; } 62 | 63 | std::string mname() { return m; } 64 | 65 | std::string sname() { return s; } 66 | 67 | // do chown 68 | int grant(uid_t, gid_t, mode_t); 69 | 70 | const char* why(); 71 | }; 72 | 73 | class pty98 : public pty { 74 | public: 75 | pty98() : pty() {} 76 | 77 | virtual ~pty98() {} 78 | 79 | 80 | pty98(const pty98 &); 81 | 82 | pty98 &operator=(const pty98 &); 83 | 84 | virtual int open(); 85 | }; 86 | 87 | } 88 | 89 | #endif 90 | -------------------------------------------------------------------------------- /src/pty98.cc: -------------------------------------------------------------------------------- 1 | /* 2 | * This file is part of port shell crypter (psc). 3 | * 4 | * (C) 2006-2020 by Sebastian Krahmer, 5 | * sebastian [dot] krahmer [at] gmail [dot] com 6 | * 7 | * psc is free software: you can redistribute it and/or modify 8 | * it under the terms of the GNU General Public License as published by 9 | * the Free Software Foundation, either version 3 of the License, or 10 | * (at your option) any later version. 11 | * 12 | * psc is distributed in the hope that it will be useful, 13 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 14 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 | * GNU General Public License for more details. 16 | * 17 | * You should have received a copy of the GNU General Public License 18 | * along with psc. If not, see . 19 | */ 20 | 21 | #include "pty.h" 22 | #include 23 | #include 24 | #include 25 | #include 26 | #include 27 | #include 28 | #include 29 | #include 30 | #include 31 | 32 | #ifdef __sun__ 33 | #include 34 | #include 35 | #include 36 | #endif 37 | 38 | namespace ns_psc { 39 | 40 | 41 | pty98::pty98(const pty98 &rhs) 42 | : pty(rhs) 43 | { 44 | } 45 | 46 | pty98 &pty98::operator=(const pty98 &rhs) 47 | { 48 | pty::operator=(rhs); 49 | return *this; 50 | } 51 | 52 | int pty98::open() 53 | { 54 | #ifdef HAVE_UNIX98 55 | m = "/dev/ptmx"; 56 | 57 | if ((_master = ::open(m.c_str(), O_RDWR|O_NOCTTY)) < 0) { 58 | serr = strerror(errno); 59 | return -1; 60 | } 61 | if (grantpt(_master) < 0) { 62 | ::close(_master); 63 | serr = strerror(errno); 64 | return -1; 65 | } 66 | 67 | unlockpt(_master); 68 | #ifdef __linux__ 69 | char buf[1024]; 70 | memset(buf, 0, sizeof(buf)); 71 | ptsname_r(_master, buf, sizeof(buf)); 72 | s = buf; 73 | #else 74 | s = ptsname(_master); 75 | #endif 76 | 77 | if ((_slave = ::open(s.c_str(), O_RDWR|O_NOCTTY)) < 0) { 78 | ::close(_master); 79 | serr = strerror(errno); 80 | return -1; 81 | } 82 | #ifdef __sun__ 83 | ioctl(_slave, I_PUSH, "ptem"); 84 | ioctl(_slave, I_PUSH, "ldterm"); 85 | ioctl(_slave, I_PUSH, "ttcompat"); 86 | #endif 87 | 88 | fchmod(_slave, 0600); 89 | #endif 90 | return 0; 91 | } 92 | 93 | } 94 | 95 | -------------------------------------------------------------------------------- /src/script_/helloworld: -------------------------------------------------------------------------------- 1 | unset HISTFILE 2 | for i in 1 2 3; do 3 | echo "Hello world $i!" 4 | done 5 | echo "plz encode"|./pscr -E /dev/stdin 6 | 7 | -------------------------------------------------------------------------------- /src/server.cc: -------------------------------------------------------------------------------- 1 | /* 2 | * This file is part of port shell crypter (psc). 3 | * 4 | * (C) 2006-2024 by Sebastian Krahmer, 5 | * sebastian [dot] krahmer [at] gmail [dot] com 6 | * 7 | * psc is free software: you can redistribute it and/or modify 8 | * it under the terms of the GNU General Public License as published by 9 | * the Free Software Foundation, either version 3 of the License, or 10 | * (at your option) any later version. 11 | * 12 | * psc is distributed in the hope that it will be useful, 13 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 14 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 | * GNU General Public License for more details. 16 | * 17 | * You should have received a copy of the GNU General Public License 18 | * along with psc. If not, see . 19 | */ 20 | 21 | #include 22 | #include 23 | #include 24 | #include 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 | #include "net.h" 43 | #include "pty.h" 44 | #include "misc.h" 45 | #include "pcwrap.h" 46 | 47 | 48 | using namespace std; 49 | using namespace ns_psc; 50 | 51 | namespace ns_psc { 52 | 53 | struct termios exit_tattr; 54 | bool exit_attr_set = 0; 55 | 56 | } 57 | 58 | const string banner = "\nPortShellCrypter [pscr] v0.69 (C) 2006-2024 stealth -- github.com/stealth/psc\n\n"; 59 | 60 | 61 | // magic banner to start encryption. If changed here, also change in pcwrap.cc 62 | const string PSC_STARTTLS = START_BANNER; 63 | 64 | 65 | // child == bash exited, send end-sequence 66 | // so psc-local can reset its crypto state 67 | void sig_chld(int) 68 | { 69 | // empty, just set to get an EINTR 70 | } 71 | 72 | 73 | int proxy_loop() 74 | { 75 | #ifdef HAVE_UNIX98 76 | pty98 pt; 77 | #else 78 | pty pt; 79 | #endif 80 | pid_t pid; 81 | int r, i; 82 | char sbuf[BLOCK_SIZE/2] = {0}; // 1 MTU 83 | struct termios tattr; 84 | 85 | if (pt.open() < 0) 86 | die(pt.why()); 87 | 88 | fix_size(pt.slave()); 89 | 90 | if (tcgetattr(fileno(stdin), &tattr) >= 0) { 91 | exit_tattr = tattr; 92 | 93 | //tattr.c_lflag &= ~ECHO; 94 | cfmakeraw(&tattr); 95 | 96 | /* May fails when we are on a portshell */ 97 | tcsetattr(fileno(stdin), TCSANOW, &tattr); 98 | exit_attr_set = 1; 99 | } 100 | 101 | struct sigaction sa; 102 | memset(&sa, 0, sizeof(sa)); 103 | sa.sa_handler = sig_chld; 104 | sa.sa_flags = SA_RESTART; 105 | 106 | if (sigaction(SIGCHLD, &sa, nullptr) < 0) 107 | die("pscr: sigaction"); 108 | 109 | sa.sa_handler = SIG_IGN; 110 | if (sigaction(SIGPIPE, &sa, nullptr) < 0) 111 | die("pscr: sigaction"); 112 | 113 | if ((pid = fork()) == 0) { 114 | char *a[] = {getenv("SHELL"), nullptr}; 115 | extern char **environ; 116 | 117 | if (!*a) { 118 | die("pscr: No shell set via $SHELL"); 119 | } 120 | 121 | // someone is using it as login-shell? 122 | if (strstr(a[0], "psc")) 123 | a[0] = strdup("/bin/sh"); 124 | 125 | dup2(pt.slave(), 0); dup2(pt.slave(), 1); 126 | dup2(pt.slave(), 2); 127 | setsid(); 128 | ioctl(0, TIOCSCTTY, 0); 129 | pt.close(); 130 | 131 | execve(*a, a, environ); 132 | die("pscr: execve"); 133 | } else if (pid < 0) 134 | die("pscr: fork"); 135 | 136 | close(pt.slave()); 137 | 138 | pc_wrap psc(0, 1); 139 | if (psc.init(PSC_READ_KEY, PSC_WRITE_KEY, 1) < 0) 140 | die(psc.why()); 141 | 142 | printf("%s%s", PSC_STARTTLS.c_str(), psc.get_iv()); 143 | if (psc.enable_crypto() < 0) 144 | die(psc.why()); 145 | 146 | struct rlimit rl; 147 | if (getrlimit(RLIMIT_NOFILE, &rl) < 0) 148 | die("pscr: getrlimit"); 149 | 150 | struct pollfd *pfds = new (nothrow) pollfd[rl.rlim_cur]; 151 | struct state *fd2state = new (nothrow) state[rl.rlim_cur]; 152 | 153 | fd2state[0].fd = 0; 154 | fd2state[0].state = STATE_STDIN; 155 | 156 | fd2state[1].fd = 1; 157 | fd2state[1].state = STATE_STDOUT; 158 | 159 | fd2state[pt.master()].fd = pt.master(); 160 | fd2state[pt.master()].state = STATE_PTY; 161 | 162 | for (unsigned int i = 0; i < rl.rlim_cur; ++i) { 163 | pfds[i].fd = -1; 164 | pfds[i].events = pfds[i].revents = 0; 165 | } 166 | 167 | pfds[0].fd = 0; 168 | pfds[0].events = POLLIN; 169 | pfds[1].fd = 1; 170 | pfds[pt.master()].fd = pt.master(); 171 | pfds[pt.master()].events = POLLIN; 172 | 173 | int max_fd = rl.rlim_cur - 1; 174 | 175 | bool breakout = 0; 176 | string ext_cmd = "", tbuf = ""; 177 | 178 | enum { CHUNK_SIZE = 8192 }; 179 | 180 | #ifdef RESPECT_UART_BUFSIZE 181 | uint32_t tx_rate_cnt = 0; 182 | #endif 183 | 184 | timeval last_tv; 185 | memset(&last_tv, 0, sizeof(last_tv)); 186 | 187 | do { 188 | memset(sbuf, 0, sizeof(sbuf)); 189 | 190 | for (i = rl.rlim_cur - 1; i > 0; --i) { 191 | if (fd2state[i].state != STATE_INVALID && fd2state[i].fd != -1) { 192 | max_fd = i; 193 | break; 194 | } 195 | } 196 | 197 | if ((r = poll(pfds, max_fd + 1, 1000)) < 0) { 198 | if (errno == EINTR) 199 | continue; 200 | else 201 | break; 202 | } 203 | 204 | time_t now = time(nullptr); 205 | 206 | for (i = 0; i <= max_fd; ++i) { 207 | 208 | if (fd2state[i].state == STATE_INVALID) 209 | continue; 210 | 211 | if ((fd2state[i].state == STATE_CLOSING && (now - fd2state[i].time) > CLOSING_TIME) || 212 | (fd2state[i].state == STATE_UDPCLIENT && (now - fd2state[i].time) > UDP_CLOSING_TIME && fd2state[i].odgrams.empty())) { 213 | close(i); 214 | fd2state[i].fd = -1; 215 | fd2state[i].state = STATE_INVALID; 216 | fd2state[i].obuf.clear(); 217 | pfds[i].fd = -1; 218 | pfds[i].events = 0; 219 | continue; 220 | } 221 | 222 | if (pfds[i].revents & (POLLERR|POLLHUP|POLLNVAL)) { 223 | if (fd2state[i].state == STATE_STDOUT || fd2state[i].state == STATE_PTY) { 224 | breakout = 1; 225 | break; 226 | } 227 | if (fd2state[i].state == STATE_CONNECTED || fd2state[i].state == STATE_CONNECT) { 228 | pfds[1].events |= POLLOUT; 229 | fd2state[1].obuf += psc.possibly_b64encrypt("C:T:F:", fd2state[i].rnode); // signal finished connection to remote 230 | tcp_nodes2sock.erase(fd2state[i].rnode); 231 | } 232 | 233 | close(i); 234 | fd2state[i].fd = -1; 235 | fd2state[i].state = STATE_INVALID; 236 | fd2state[i].obuf.clear(); 237 | pfds[i].fd = -1; 238 | pfds[i].events = 0; 239 | continue; 240 | } 241 | 242 | ext_cmd.clear(); 243 | 244 | if (pfds[i].revents & POLLIN) { 245 | pfds[i].revents = 0; 246 | if (fd2state[i].state == STATE_PTY) { 247 | // read into small buf 248 | if ((r = read(pt.master(), sbuf, sizeof(sbuf))) <= 0) { 249 | breakout = 1; 250 | break; 251 | } 252 | fd2state[i].time = now; 253 | fd2state[1].time = now; 254 | fd2state[1].obuf += psc.possibly_b64encrypt("D:0:", string(sbuf, r)); 255 | pfds[1].events |= POLLOUT; 256 | 257 | } else if (fd2state[i].state == STATE_STDIN) { 258 | int starttls = 0, nosys = 0; 259 | do { 260 | tbuf = ext_cmd = ""; 261 | if ((r = psc.read(nosys, tbuf, ext_cmd, starttls)) < 0) { 262 | breakout = 1; 263 | break; 264 | } 265 | nosys = 1; 266 | psc.check_wsize(pt.master()); 267 | 268 | if (ext_cmd.size() > 0) 269 | cmd_handler(ext_cmd, fd2state, pfds, NETCMD_SEND_ALLOW); 270 | else if (tbuf.size() > 0) { 271 | fd2state[pt.master()].time = now; 272 | fd2state[pt.master()].obuf += tbuf; 273 | pfds[pt.master()].events |= POLLOUT; 274 | } 275 | } while (r == 1); 276 | 277 | } else if (fd2state[i].state == STATE_CONNECTED) { 278 | if (config::rate_limit_bytes && fd2state[1].obuf.size() > MAX_RX_ON_LIMITS) 279 | continue; 280 | if ((r = recv(i, sbuf, sizeof(sbuf), 0)) <= 0) { 281 | close(i); 282 | pfds[i].fd = -1; 283 | pfds[i].events = 0; 284 | fd2state[i].state = STATE_INVALID; 285 | fd2state[i].fd = -1; 286 | fd2state[i].obuf.clear(); 287 | tcp_nodes2sock.erase(fd2state[i].rnode); 288 | 289 | pfds[1].events |= POLLOUT; 290 | fd2state[1].obuf += psc.possibly_b64encrypt("C:T:F:", fd2state[i].rnode); // signal finished connection via stdout to remote 291 | continue; 292 | } 293 | fd2state[i].time = now; 294 | 295 | pfds[1].events |= POLLOUT; 296 | fd2state[1].obuf += psc.possibly_b64encrypt("C:T:R:", fd2state[i].rnode + string(sbuf, r)); // received TCP data 297 | } else if (fd2state[i].state == STATE_UDPCLIENT) { 298 | if ((r = recv(i, sbuf, sizeof(sbuf), 0)) <= 0) 299 | continue; 300 | 301 | fd2state[i].time = now; 302 | 303 | pfds[1].events |= POLLOUT; 304 | fd2state[1].obuf += psc.possibly_b64encrypt("C:U:R:", fd2state[i].rnode + string(sbuf, r)); // received UDP data 305 | } 306 | } else if (pfds[i].revents & POLLOUT) { 307 | pfds[i].revents = 0; 308 | size_t n = fd2state[i].obuf.size() > CHUNK_SIZE ? CHUNK_SIZE : fd2state[i].obuf.size(); 309 | 310 | if (fd2state[i].state == STATE_PTY) { 311 | if ((r = write(pt.master(), fd2state[i].obuf.c_str(), n)) <= 0) { 312 | breakout = 1; 313 | break; 314 | } 315 | 316 | fd2state[i].time = now; 317 | fd2state[i].obuf.erase(0, r); 318 | } else if (fd2state[i].state == STATE_STDOUT) { 319 | 320 | // We need to throttle amount of data/usec in cases where bounce command was 321 | // given since remote pty is in raw mode w/o flow control or we run across a serial 322 | // line that has a baud rate set 323 | if (config::rate_limit_bytes) { 324 | n = 1; 325 | 326 | timeval now_tv; 327 | gettimeofday(&now_tv, nullptr); 328 | 329 | // In usec. 330 | uint64_t tdiff_usec = now_tv.tv_sec*1000000 + now_tv.tv_usec - (last_tv.tv_sec*1000000 + last_tv.tv_usec); 331 | 332 | // This formula only works for rates < 1.000.000 Byte/sec which is guaranteed by the baud rates that we accept 333 | if (tdiff_usec < (1000000*1.0/config::rate_limit_bytes)) 334 | continue; 335 | #if RESPECT_UART_BUFSIZE 336 | // Try to give UART buffers time to get flushed. 337 | if (++tx_rate_cnt >= RESPECT_UART_BUFSIZE) { 338 | if (tdiff_usec < 1000000) 339 | continue; 340 | else 341 | tx_rate_cnt = 0; 342 | } 343 | #endif 344 | 345 | last_tv.tv_sec = now_tv.tv_sec; 346 | last_tv.tv_usec = now_tv.tv_usec; 347 | 348 | // It fits into limit, go ahead with writing. 349 | } 350 | 351 | if ((r = write(psc.w_fileno(), fd2state[i].obuf.c_str(), n)) <= 0) { 352 | breakout = 1; 353 | break; 354 | } 355 | 356 | fd2state[i].time = now; 357 | fd2state[i].obuf.erase(0, r); 358 | } else if (fd2state[i].state == STATE_CONNECT) { 359 | int e = 0; 360 | socklen_t elen = sizeof(e); 361 | if (getsockopt(i, SOL_SOCKET, SO_ERROR, &e, &elen) < 0 || e != 0) { 362 | close(i); 363 | pfds[i].fd = -1; 364 | pfds[i].events = 0; 365 | fd2state[i].state = STATE_INVALID; 366 | fd2state[i].fd = -1; 367 | fd2state[i].obuf.clear(); 368 | tcp_nodes2sock.erase(fd2state[i].rnode); 369 | 370 | pfds[1].events |= POLLOUT; 371 | fd2state[1].obuf += psc.possibly_b64encrypt("C:T:F:", fd2state[i].rnode); // signal finished connection via stdout to remote 372 | continue; 373 | } 374 | 375 | maybe_set_rcvbuf(i, config::rate_limit_bytes); 376 | 377 | pfds[i].events = POLLIN; 378 | fd2state[i].state = STATE_CONNECTED; 379 | fd2state[i].time = now; 380 | 381 | pfds[1].events |= POLLOUT; 382 | fd2state[1].obuf += psc.possibly_b64encrypt("C:T:C:", fd2state[i].rnode); // TCP connect() finished, connection is set up 383 | 384 | } else if (fd2state[i].state == STATE_CONNECTED) { 385 | if ((r = send(i, fd2state[i].obuf.c_str(), n, 0)) <= 0) { 386 | close(i); 387 | pfds[i].fd = -1; 388 | pfds[i].events = 0; 389 | fd2state[i].state = STATE_INVALID; 390 | fd2state[i].fd = -1; 391 | fd2state[i].obuf.clear(); 392 | tcp_nodes2sock.erase(fd2state[i].rnode); 393 | 394 | pfds[1].events |= POLLOUT; 395 | fd2state[1].obuf += psc.possibly_b64encrypt("C:T:F:", fd2state[i].rnode); // signal finished connection via stdout to remote 396 | continue; 397 | } 398 | 399 | fd2state[i].time = now; 400 | fd2state[i].obuf.erase(0, r); 401 | } else if (fd2state[i].state == STATE_UDPCLIENT) { 402 | string &dgram = fd2state[i].odgrams.front().second; 403 | // No need to sendto(), each socket with ID is connect()'ed since -U binding already knows 404 | // the remote IP:port to send to 405 | if ((r = send(i, dgram.c_str(), dgram.size(), 0)) <= 0) 406 | continue; 407 | 408 | fd2state[i].time = now; 409 | fd2state[i].odgrams.pop_front(); 410 | } 411 | 412 | if (fd2state[i].obuf.empty() && fd2state[i].odgrams.empty()) 413 | pfds[i].events &= ~POLLOUT; 414 | } 415 | } 416 | } while (!breakout); 417 | 418 | if (exit_attr_set) 419 | tcsetattr(fileno(stdin), TCSANOW, &exit_tattr); 420 | 421 | // send any left input data along with exit cmd 422 | string ex = fd2state[psc.w_fileno()].obuf; 423 | fd2state[psc.w_fileno()].obuf.clear(); 424 | ex += psc.possibly_b64encrypt("C:", "exit:0"); 425 | writen(psc.w_fileno(), ex.c_str(), ex.size()); 426 | return 0; 427 | } 428 | 429 | 430 | int b64_encode_file(const string &path) 431 | { 432 | int fd; 433 | if ((fd = open(path.c_str(), O_RDONLY|O_NOCTTY)) < 0) 434 | return -1; 435 | 436 | char buf[4096] = {0}; 437 | string data = ""; 438 | 439 | for (size_t r = 0;;) { 440 | if ((r = read(fd, buf, sizeof(buf))) <= 0) 441 | break; 442 | data += string(buf, r); 443 | } 444 | 445 | close(fd); 446 | 447 | unique_ptr b64(new (nothrow) unsigned char[2*data.size()]); 448 | if (!b64.get()) 449 | return -1; 450 | 451 | string b64_str = b64_encode(data.c_str(), data.size(), b64.get()); 452 | 453 | printf("begin-base64 600 %s\n", path.c_str()); 454 | while (b64_str.size() > 0) { 455 | printf("%s\n", b64_str.substr(0, 60).c_str()); 456 | b64_str.erase(0, 60); 457 | } 458 | printf("====\n"); 459 | 460 | return 0; 461 | } 462 | 463 | 464 | // decode from stdin 465 | int b64_decode_file() 466 | { 467 | char buf[4096] = {0}; 468 | string data = "", marker = "begin-base64 600 ", hdr = ""; 469 | string::size_type idx; 470 | 471 | for (;;) { 472 | if (!fgets(buf, sizeof(buf) - 1, stdin)) 473 | return -1; 474 | hdr = buf; 475 | if ((idx = hdr.find(marker)) != string::npos) 476 | break; 477 | } 478 | 479 | string path = hdr.substr(marker.size()); 480 | if ((idx = path.rfind("/")) != string::npos) 481 | path.erase(0, idx + 1); 482 | if ((idx = path.find("\n")) != string::npos) 483 | path.erase(idx, 1); 484 | if ((idx = path.find("\r")) != string::npos) 485 | path.erase(idx, 1); 486 | 487 | path = "_b64." + path; 488 | 489 | for (size_t r = 0;;) { 490 | if ((r = read(0, buf, sizeof(buf))) <= 0) 491 | break; 492 | data += string(buf, r); 493 | if (data.find("\n====") != string::npos) 494 | break; 495 | } 496 | 497 | if ((idx = data.find("\n====")) != string::npos) 498 | data.erase(idx); 499 | 500 | // erase all new-lines 501 | data.erase(remove(data.begin(), data.end(), '\r'), data.end()); 502 | data.erase(remove(data.begin(), data.end(), '\n'), data.end()); 503 | 504 | unique_ptr b64(new (nothrow) unsigned char[data.size()]); 505 | if (!b64.get()) 506 | return -1; 507 | 508 | auto dec_len = b64_decode(data.c_str(), b64.get()); 509 | 510 | int fd = open(path.c_str(), O_CREAT|O_WRONLY|O_EXCL, 0600); 511 | if (fd < 0) 512 | return -1; 513 | 514 | if (write(fd, b64.get(), dec_len) < 0) 515 | ; // avoid gcc warning about unused retval 516 | 517 | close(fd); 518 | 519 | return 0; 520 | } 521 | 522 | 523 | void usage(const char *argv0) 524 | { 525 | printf("%sUsage: %s [-E file] [-D] [-N] [-l baud limit]\n", banner.c_str(), argv0); 526 | } 527 | 528 | 529 | int main(int argc, char **argv) 530 | { 531 | setvbuf(stdin, nullptr, _IONBF, 0); 532 | setvbuf(stdout, nullptr, _IONBF, 0); 533 | setvbuf(stderr, nullptr, _IONBF, 0); 534 | 535 | int c = 0; 536 | bool no_nagle = 0, b64_encoded = 0, invalid_rate = 0; 537 | string bauds = ""; 538 | 539 | while ((c = getopt(argc, argv, "E:l:DNh")) != -1) { 540 | 541 | switch (c) { 542 | case 'N': 543 | no_nagle = 1; 544 | break; 545 | case 'E': 546 | b64_encode_file(optarg); 547 | b64_encoded = 1; 548 | break; 549 | case 'D': 550 | b64_decode_file(); 551 | exit(0); 552 | break; 553 | case 'l': 554 | if (config_set_baud_limit(optarg) < 0) 555 | invalid_rate = 1; 556 | break; 557 | case 'h': 558 | default: 559 | usage(argv[0]); 560 | exit(0); 561 | } 562 | } 563 | 564 | if (!b64_encoded) { 565 | printf("%s", banner.c_str()); 566 | 567 | if (invalid_rate) 568 | printf("pscr: Invalid baud rate. Must be one of 576000, 230400, 115200, 57600,\n" 569 | "pscr: 38400, 9600 or 0.\n"); 570 | 571 | if (!getenv("SHELL")) { 572 | printf("pscr: No $SHELL set in environment. Exiting.\n"); 573 | exit(1); 574 | } 575 | } 576 | 577 | // disable nagle if stdout is a socket 578 | if (no_nagle) { 579 | int one = 1; 580 | socklen_t len = sizeof(one); 581 | setsockopt(1, IPPROTO_TCP, TCP_NODELAY, &one, len); 582 | } 583 | 584 | if (b64_encoded) 585 | return 0; 586 | 587 | proxy_loop(); 588 | 589 | return 0; 590 | } 591 | 592 | --------------------------------------------------------------------------------