zanzibar
1 year ago
108 changed files with 8210 additions and 2169 deletions
@ -1,6 +1,11 @@ |
|||
185.25.48.236:27485 |
|||
185.25.48.236:27487 |
|||
185.64.105.111:27485 |
|||
185.64.105.111:27487 |
|||
185.25.48.72:27485 |
|||
185.25.48.72:27487 |
|||
# node1.hush.land |
|||
185.241.61.43 |
|||
|
|||
# node2.hush.is |
|||
137.74.4.198 |
|||
|
|||
# lite.hushpool.is |
|||
149.28.102.219 |
|||
|
|||
# todo: torv3 |
|||
# todo: i2p |
|||
|
@ -0,0 +1,161 @@ |
|||
# I2P support in Hush |
|||
|
|||
It is possible to run a Hush or HSC full node as an |
|||
[I2P (Invisible Internet Project)](https://en.wikipedia.org/wiki/I2P) |
|||
service and connect to such services. |
|||
|
|||
This [glossary](https://geti2p.net/en/about/glossary) may be useful to get |
|||
started with I2P terminology. |
|||
|
|||
## Run with an I2P router (proxy) |
|||
|
|||
A running I2P router (proxy) with [SAM](https://geti2p.net/en/docs/api/samv3) |
|||
enabled is required. Options include: |
|||
|
|||
- [i2prouter (I2P Router)](https://geti2p.net), the official implementation in |
|||
Java |
|||
- [i2pd (I2P Daemon)](https://github.com/PurpleI2P/i2pd) |
|||
([documentation](https://i2pd.readthedocs.io/en/latest)), a lighter |
|||
alternative in C++ (successfully tested with version 2.23 and up; version 2.36 |
|||
or later recommended) |
|||
- [i2p-zero](https://github.com/i2p-zero/i2p-zero) |
|||
- [other alternatives](https://en.wikipedia.org/wiki/I2P#Routers) |
|||
|
|||
Note the IP address and port the SAM proxy is listening to; usually, it is |
|||
`127.0.0.1:7656`. |
|||
|
|||
Once an I2P router with SAM enabled is up and running, use the following |
|||
configuration options: |
|||
|
|||
``` |
|||
-i2psam=<ip:port> |
|||
I2P SAM proxy to reach I2P peers and accept I2P connections (default: |
|||
none) |
|||
|
|||
-i2pacceptincoming |
|||
If set and -i2psam is also set then incoming I2P connections are |
|||
accepted via the SAM proxy. If this is not set but -i2psam is set |
|||
then only outgoing connections will be made to the I2P network. |
|||
Ignored if -i2psam is not set. Listening for incoming I2P |
|||
connections is done through the SAM proxy, not by binding to a |
|||
local address and port (default: 1) |
|||
``` |
|||
|
|||
In a typical situation, this suffices: |
|||
|
|||
``` |
|||
hushd -i2psam=127.0.0.1:7656 |
|||
``` |
|||
|
|||
The first time hushd connects to the I2P router, if |
|||
`-i2pacceptincoming=1`, then it will automatically generate a persistent I2P |
|||
address and its corresponding private key. The private key will be saved in a |
|||
file named `i2p_private_key` in the Hush data directory. The persistent |
|||
I2P address is used for accepting incoming connections and for making outgoing |
|||
connections if `-i2pacceptincoming=1`. If `-i2pacceptincoming=0` then only |
|||
outbound I2P connections are made and a different transient I2P address is used |
|||
for each connection to improve privacy. |
|||
|
|||
## Persistent vs transient I2P addresses |
|||
|
|||
In I2P connections, the connection receiver sees the I2P address of the |
|||
connection initiator. This is unlike the Tor network where the recipient does |
|||
not know who is connecting to them and can't tell if two connections are from |
|||
the same peer or not. |
|||
|
|||
If an I2P node is not accepting incoming connections, then Hush uses |
|||
random, one-time, transient I2P addresses for itself for outbound connections |
|||
to make it harder to discriminate, fingerprint or analyze it based on its I2P |
|||
address. |
|||
|
|||
## Additional configuration options related to I2P |
|||
|
|||
``` |
|||
-debug=i2p |
|||
``` |
|||
|
|||
Set the `debug=i2p` config logging option to see additional information in the |
|||
debug log about your I2P configuration and connections. Run `hush-cli help |
|||
logging` for more information. |
|||
|
|||
``` |
|||
-onlynet=i2p |
|||
``` |
|||
|
|||
Make automatic outbound connections only to I2P addresses. Inbound and manual |
|||
connections are not affected by this option. It can be specified multiple times |
|||
to allow multiple networks, e.g. onlynet=onion, onlynet=i2p. |
|||
|
|||
I2P support was added to Hush in version 3.9.3 and there may be fewer I2P |
|||
peers than Tor or IP ones. Therefore, using I2P alone without other networks may |
|||
make a node more susceptible to [Sybil |
|||
attacks](https://en.bitcoin.it/wiki/Weaknesses#Sybil_attack). You can use |
|||
`hush-cli -addrinfo` to see the number of I2P addresses known to your node. |
|||
|
|||
Another consideration with `onlynet=i2p` is that the initial blocks download |
|||
phase when syncing up a new node can be very slow. This phase can be sped up by |
|||
using other networks, for instance `onlynet=onion`, at the same time. |
|||
|
|||
In general, a node can be run with both onion and I2P hidden services (or |
|||
any/all of IPv4/IPv6/onion/I2P/CJDNS), which can provide a potential fallback if |
|||
one of the networks has issues. |
|||
|
|||
## I2P-related information |
|||
|
|||
There are several ways to see your I2P address if accepting |
|||
incoming I2P connections (`-i2pacceptincoming`): |
|||
- in the "Local addresses" output of CLI `-netinfo` |
|||
- in the "localaddresses" output of RPC `getnetworkinfo` |
|||
- in the debug log (grep for `AddLocal`; the I2P address ends in `.b32.i2p`) |
|||
|
|||
To see which I2P peers your node is connected to, use `hush-cli -netinfo 4` |
|||
or the `getpeerinfo` RPC (e.g. `hush-cli getpeerinfo`). |
|||
|
|||
To see which I2P addresses your node knows, use the `getnodeaddresses 0 i2p` |
|||
RPC. |
|||
|
|||
## Compatibility |
|||
|
|||
Hush uses the [SAM v3.1](https://geti2p.net/en/docs/api/samv3) protocol |
|||
to connect to the I2P network. Any I2P router that supports it can be used. |
|||
|
|||
## Ports in I2P and Hush |
|||
|
|||
Hush uses the [SAM v3.1](https://geti2p.net/en/docs/api/samv3) |
|||
protocol. One particularity of SAM v3.1 is that it does not support ports, |
|||
unlike newer versions of SAM (v3.2 and up) that do support them and default the |
|||
port numbers to 0. From the point of view of peers that use newer versions of |
|||
SAM or other protocols that support ports, a SAM v3.1 peer is connecting to them |
|||
on port 0, from source port 0. |
|||
|
|||
To allow future upgrades to newer versions of SAM, Hush sets its |
|||
listening port to 0 when listening for incoming I2P connections and advertises |
|||
its own I2P address with port 0. Furthermore, it will not attempt to connect to |
|||
I2P addresses with a non-zero port number because with SAM v3.1 the destination |
|||
port (`TO_PORT`) is always set to 0 and is not in the control of Hush. |
|||
|
|||
## Bandwidth |
|||
|
|||
I2P routers may route a large amount of general network traffic with their |
|||
default settings. Check your router's configuration to limit the amount of this |
|||
traffic relayed, if desired. |
|||
|
|||
With `i2pd`, the amount of bandwidth being shared with the wider network can be |
|||
adjusted with the `bandwidth`, `share` and `transittunnels` options in your |
|||
`i2pd.conf` file. For example, to limit total I2P traffic to 256KB/s and share |
|||
50% of this limit for a maximum of 20 transit tunnels: |
|||
|
|||
``` |
|||
bandwidth = 256 |
|||
share = 50 |
|||
|
|||
[limits] |
|||
transittunnels = 20 |
|||
``` |
|||
|
|||
If you prefer not to relay any public I2P traffic and only permit I2P traffic |
|||
from programs which are connecting via the SAM proxy, e.g. Hush, you |
|||
can set the `notransit` option to `true`. |
|||
|
|||
Similar bandwidth configuration options for the Java I2P router can be found in |
|||
`http://127.0.0.1:7657/config` under the "Bandwidth" tab. |
@ -1,154 +1,225 @@ |
|||
# Warning |
|||
# Tor |
|||
|
|||
Do not assume Tor support works perfectly in Hush; better Tor support is currently being worked on. |
|||
|
|||
# Hush + Tor |
|||
|
|||
It is possible to run Hush as a Tor hidden service, and connect to such services. |
|||
It is possible to run Hush as a Tor onion service, and connect to such services. |
|||
|
|||
The following directions assume you have a Tor proxy running on port 9050. Many distributions default to having a SOCKS proxy listening on port 9050, but others may not. In particular, the Tor Browser Bundle defaults to listening on port 9150. See [Tor Project FAQ:TBBSocksPort](https://www.torproject.org/docs/faq.html.en#TBBSocksPort) for how to properly |
|||
configure Tor. |
|||
|
|||
## Compatibility |
|||
|
|||
- Starting with version 3.9.3, Hush only supports Tor version 3 hidden |
|||
services (Tor v3). Tor v2 addresses are ignored by Hush and neither |
|||
relayed nor stored. |
|||
|
|||
- Tor removed v2 support beginning with version 0.4.6. |
|||
|
|||
## How to see information about your Tor configuration via Hush |
|||
|
|||
There are several ways to see your local onion address in Hush: |
|||
- in the "Local addresses" output of CLI `-netinfo` |
|||
- in the "localaddresses" output of RPC `getnetworkinfo` |
|||
- in the debug log (grep for "AddLocal"; the Tor address ends in `.onion`) |
|||
|
|||
You may set the `-debug=tor` config logging option to have additional |
|||
information in the debug log about your Tor configuration. |
|||
|
|||
CLI `-addrinfo` returns the number of addresses known to your node per |
|||
network. This can be useful to see how many onion peers your node knows, |
|||
e.g. for `-onlynet=onion`. |
|||
|
|||
1. Run Hush behind a Tor proxy |
|||
------------------------------- |
|||
To fetch a number of onion addresses that your node knows, for example seven |
|||
addresses, use the `getnodeaddresses 7 onion` RPC. |
|||
|
|||
The first step is running Hush behind a Tor proxy. This will already make all |
|||
outgoing connections be anonymized, but more is possible. |
|||
## 1. Run Hush behind a Tor proxy |
|||
|
|||
-proxy=ip:port Set the proxy server. If SOCKS5 is selected (default), this proxy |
|||
server will be used to try to reach .onion addresses as well. |
|||
The first step is running Hush behind a Tor proxy. This will already anonymize all |
|||
outgoing connections, but more is possible. |
|||
|
|||
-onion=ip:port Set the proxy server to use for Tor hidden services. You do not |
|||
need to set this if it's the same as -proxy. You can use -noonion |
|||
to explicitly disable access to hidden service. |
|||
-proxy=ip:port Set the proxy server. If SOCKS5 is selected (default), this proxy |
|||
server will be used to try to reach .onion addresses as well. |
|||
You need to use -noonion or -onion=0 to explicitly disable |
|||
outbound access to onion services. |
|||
|
|||
-listen When using -proxy, listening is disabled by default. If you want |
|||
to run a hidden service (see next section), you'll need to enable |
|||
it explicitly. |
|||
-onion=ip:port Set the proxy server to use for Tor onion services. You do not |
|||
need to set this if it's the same as -proxy. You can use -onion=0 |
|||
to explicitly disable access to onion services. |
|||
------------------------------------------------------------------ |
|||
Note: Only the -proxy option sets the proxy for DNS requests; |
|||
with -onion they will not route over Tor, so use -proxy if you |
|||
have privacy concerns. |
|||
------------------------------------------------------------------ |
|||
|
|||
-connect=X When behind a Tor proxy, you can specify .onion addresses instead |
|||
-addnode=X of IP addresses or hostnames in these parameters. It requires |
|||
-seednode=X SOCKS5. In Tor mode, such addresses can also be exchanged with |
|||
other P2P nodes. |
|||
-listen When using -proxy, listening is disabled by default. If you want |
|||
to manually configure an onion service (see section 3), you'll |
|||
need to enable it explicitly. |
|||
|
|||
-connect=X When behind a Tor proxy, you can specify .onion addresses instead |
|||
-addnode=X of IP addresses or hostnames in these parameters. It requires |
|||
-seednode=X SOCKS5. In Tor mode, such addresses can also be exchanged with |
|||
other P2P nodes. |
|||
|
|||
-onlynet=onion Make automatic outbound connections only to .onion addresses. |
|||
Inbound and manual connections are not affected by this option. |
|||
It can be specified multiple times to allow multiple networks, |
|||
e.g. onlynet=onion, onlynet=i2p, onlynet=cjdns. |
|||
|
|||
In a typical situation, this suffices to run behind a Tor proxy: |
|||
|
|||
./hushd -proxy=127.0.0.1:9050 |
|||
./hushd -proxy=127.0.0.1:9050 |
|||
|
|||
If using the Tor Browser Bundle: |
|||
## 2. Automatically create a Hush onion service |
|||
|
|||
./hushd -proxy=127.0.0.1:9150 |
|||
Hush makes use of Tor's control socket API to create and destroy |
|||
ephemeral onion services programmatically. This means that if Tor is running and |
|||
proper authentication has been configured, Hush automatically creates an |
|||
onion service to listen on. The goal is to increase the number of available |
|||
onion nodes. |
|||
|
|||
This feature is enabled by default if Hush is listening (`-listen`) and |
|||
it requires a Tor connection to work. It can be explicitly disabled with |
|||
`-listenonion=0`. If it is not disabled, it can be configured using the |
|||
`-torcontrol` and `-torpassword` settings. |
|||
|
|||
To see verbose Tor information in the hushd debug log, pass `-debug=tor`. |
|||
|
|||
2. Run a Hush hidden server |
|||
---------------------------- |
|||
### Control Port |
|||
|
|||
If you configure your Tor system accordingly, it is possible to make your node also |
|||
reachable from the Tor network. Add these lines to your /etc/tor/torrc (or equivalent |
|||
config file): |
|||
You may need to set up the Tor Control Port. On Linux distributions there may be |
|||
some or all of the following settings in `/etc/tor/torrc`, generally commented |
|||
out by default (if not, add them): |
|||
|
|||
HiddenServiceDir /var/lib/tor/hush-service/ |
|||
HiddenServicePort 18030 127.0.0.1:18030 |
|||
``` |
|||
ControlPort 9051 |
|||
CookieAuthentication 1 |
|||
CookieAuthFileGroupReadable 1 |
|||
``` |
|||
|
|||
The directory can be different of course, but (both) port numbers should be equal to |
|||
your hushd's P2P listen port (18030 by default). |
|||
Add or uncomment those, save, and restart Tor (usually `systemctl restart tor` |
|||
or `sudo systemctl restart tor` on most systemd-based systems, including recent |
|||
Debian and Ubuntu, or just restart the computer). |
|||
|
|||
-externalip=X You can tell Hush about its publicly reachable address using |
|||
this option, and this can be a .onion address. Given the above |
|||
configuration, you can find your onion address in |
|||
/var/lib/tor/hush-service/hostname. Onion addresses are given |
|||
preference for your node to advertize itself with, for connections |
|||
coming from unroutable addresses (such as 127.0.0.1, where the |
|||
Tor proxy typically runs). |
|||
On some systems (such as Arch Linux), you may also need to add the following |
|||
line: |
|||
|
|||
-listen You'll need to enable listening for incoming connections, as this |
|||
is off by default behind a proxy. |
|||
``` |
|||
DataDirectoryGroupReadable 1 |
|||
``` |
|||
|
|||
-discover When -externalip is specified, no attempt is made to discover local |
|||
IPv4 or IPv6 addresses. If you want to run a dual stack, reachable |
|||
from both Tor and IPv4 (or IPv6), you'll need to either pass your |
|||
other addresses using -externalip, or explicitly enable -discover. |
|||
Note that both addresses of a dual-stack system may be easily |
|||
linkable using traffic analysis. |
|||
### Authentication |
|||
|
|||
In a typical situation, where you're only reachable via Tor, this should suffice: |
|||
Connecting to Tor's control socket API requires one of two authentication |
|||
methods to be configured: cookie authentication or hushd's `-torpassword` |
|||
configuration option. |
|||
|
|||
./hushd -proxy=127.0.0.1:9050 -externalip=hushc0de123.onion -listen |
|||
#### Cookie authentication |
|||
|
|||
(obviously, replace the Onion address with your own). Currently only v2 HS's are supported. |
|||
It should be noted that you still listen on all devices and another node could establish a clearnet connection, when knowing |
|||
your address. To mitigate this, additionally bind the address of your Tor proxy: |
|||
For cookie authentication, the user running hushd must have read access to |
|||
the `CookieAuthFile` specified in the Tor configuration. In some cases this is |
|||
preconfigured and the creation of an onion service is automatic. Don't forget to |
|||
use the `-debug=tor` hushd configuration option to enable Tor debug logging. |
|||
|
|||
./hushd ... -bind=127.0.0.1 |
|||
If a permissions problem is seen in the debug log, e.g. `tor: Authentication |
|||
cookie /run/tor/control.authcookie could not be opened (check permissions)`, it |
|||
can be resolved by adding both the user running Tor and the user running |
|||
hushd to the same Tor group and setting permissions appropriately. |
|||
|
|||
If you don't care too much about hiding your node, and want to be reachable on IPv4 |
|||
as well, use `discover` instead: |
|||
On Debian-derived systems, the Tor group will likely be `debian-tor` and one way |
|||
to verify could be to list the groups and grep for a "tor" group name: |
|||
|
|||
./hushd ... -discover |
|||
``` |
|||
getent group | cut -d: -f1 | grep -i tor |
|||
``` |
|||
|
|||
and open port 18030 on your firewall. |
|||
You can also check the group of the cookie file. On most Linux systems, the Tor |
|||
auth cookie will usually be `/run/tor/control.authcookie`: |
|||
|
|||
If you only want to use Tor to reach onion addresses, but not use it as a proxy |
|||
for normal IPv4/IPv6 communication, use: |
|||
``` |
|||
TORGROUP=$(stat -c '%G' /run/tor/control.authcookie) |
|||
``` |
|||
|
|||
./hushd -onion=127.0.0.1:9050 -externalip=hushc0de123.onion -discover |
|||
Once you have determined the `${TORGROUP}` and selected the `${USER}` that will |
|||
run hushd, run this as root: |
|||
|
|||
``` |
|||
usermod -a -G ${TORGROUP} ${USER} |
|||
``` |
|||
|
|||
3. Automatically listen on Tor |
|||
-------------------------------- |
|||
Then restart the computer (or log out) and log in as the `${USER}` that will run |
|||
hushd. |
|||
|
|||
Starting with Tor version 0.2.7.1 it is possible, through Tor's control socket |
|||
API, to create and destroy 'ephemeral' hidden services programmatically. |
|||
Hush has been updated to make use of this. |
|||
#### `torpassword` authentication |
|||
|
|||
This means that if Tor is running (and proper authentication has been configured), |
|||
Hush automatically creates a hidden service to listen on. Hush will also use Tor |
|||
automatically to connect to other .onion nodes if the control socket can be |
|||
successfully opened. This will positively affect the number of available .onion |
|||
nodes and their usage. |
|||
For the `-torpassword=password` option, the password is the clear text form that |
|||
was used when generating the hashed password for the `HashedControlPassword` |
|||
option in the Tor configuration file. |
|||
|
|||
This new feature is enabled by default if Hush is listening (`-listen`), and |
|||
requires a Tor connection to work. It can be explicitly disabled with `-listenonion=0` |
|||
and, if not disabled, configured using the `-torcontrol` and `-torpassword` settings. |
|||
To show verbose debugging information, pass `-debug=tor`. |
|||
The hashed password can be obtained with the command `tor --hash-password |
|||
password` (refer to the [Tor Dev |
|||
Manual](https://2019.www.torproject.org/docs/tor-manual.html.en) for more |
|||
details). |
|||
|
|||
Connecting to Tor's control socket API requires one of two authentication methods to be |
|||
configured. For cookie authentication the user running hushd must have write access |
|||
to the `CookieAuthFile` specified in Tor configuration. In some cases this is |
|||
preconfigured and the creation of a hidden service is automatic. If permission problems |
|||
are seen with `-debug=tor` they can be resolved by adding both the user running tor and |
|||
the user running hushd to the same group and setting permissions appropriately. On |
|||
Debian-based systems the user running hushd can be added to the debian-tor group, |
|||
which has the appropriate permissions. An alternative authentication method is the use |
|||
of the `-torpassword` flag and a `hash-password` which can be enabled and specified in |
|||
Tor configuration. |
|||
|
|||
## 3. Manually create a Hush onion service |
|||
|
|||
4. Connect to a Hush hidden server |
|||
----------------------------------- |
|||
You can also manually configure your node to be reachable from the Tor network. |
|||
Add these lines to your `/etc/tor/torrc` (or equivalent config file): |
|||
|
|||
To test your set-up, you might want to try connecting via Tor on a different computer to just a |
|||
a single Hush hidden server. Launch hushd as follows: |
|||
HiddenServiceDir /var/lib/tor/hush-service/ |
|||
HiddenServicePort 18030 127.0.0.1:18032 |
|||
|
|||
./hushd -onion=127.0.0.1:9050 -connect=fuckzookoie6wxgio.onion |
|||
The directory can be different of course, but virtual port numbers should be equal to |
|||
your hushd's P2P listen port (18030 by default), and target addresses and ports |
|||
should be equal to binding address and port for inbound Tor connections (127.0.0.1:18032 by default). |
|||
|
|||
Now use hush-cli to verify there is only a single peer connection. |
|||
-externalip=X You can tell hush about its publicly reachable addresses using |
|||
this option, and this can be an onion address. Given the above |
|||
configuration, you can find your onion address in |
|||
/var/lib/tor/hush-service/hostname. For connections |
|||
coming from unroutable addresses (such as 127.0.0.1, where the |
|||
Tor proxy typically runs), onion addresses are given |
|||
preference for your node to advertise itself with. |
|||
|
|||
hush-cli getpeerinfo |
|||
You can set multiple local addresses with -externalip. The |
|||
one that will be rumoured to a particular peer is the most |
|||
compatible one and also using heuristics, e.g. the address |
|||
with the most incoming connections, etc. |
|||
|
|||
-listen You'll need to enable listening for incoming connections, as this |
|||
is off by default behind a proxy. |
|||
|
|||
-discover When -externalip is specified, no attempt is made to discover local |
|||
IPv4 or IPv6 addresses. If you want to run a dual stack, reachable |
|||
from both Tor and IPv4 (or IPv6), you'll need to either pass your |
|||
other addresses using -externalip, or explicitly enable -discover. |
|||
Note that both addresses of a dual-stack system may be easily |
|||
linkable using traffic analysis. |
|||
|
|||
In a typical situation, where you're only reachable via Tor, this should suffice: |
|||
|
|||
./hushd -proxy=127.0.0.1:9050 -externalip=7zvj7a2imdgkdbg4f2dryd5rgtrn7upivr5eeij4cicjh65pooxeshid.onion -listen |
|||
|
|||
(obviously, replace the .onion address with your own). It should be noted that you still |
|||
listen on all devices and another node could establish a clearnet connection, when knowing |
|||
your address. To mitigate this, additionally bind the address of your Tor proxy: |
|||
|
|||
./hushd ... -bind=127.0.0.1 |
|||
|
|||
If you don't care too much about hiding your node, and want to be reachable on IPv4 |
|||
as well, use `discover` instead: |
|||
|
|||
./hushd ... -discover |
|||
|
|||
and open port 18030 on your firewall (or use port mapping, i.e., `-upnp` or `-natpmp`). |
|||
|
|||
If you only want to use Tor to reach .onion addresses, but not use it as a proxy |
|||
for normal IPv4/IPv6 communication, use: |
|||
|
|||
[ |
|||
{ |
|||
"id" : 1, |
|||
"addr" : "zcashhoneypot.onion:18030", |
|||
... |
|||
"version" : 1987420, |
|||
"subver" : "/GoldenSandtrout:3.6.0/", |
|||
... |
|||
} |
|||
] |
|||
./hushd -onion=127.0.0.1:9050 -externalip=7zvj7a2imdgkdbg4f2dryd5rgtrn7upivr5eeij4cicjh65pooxeshid.onion -discover |
|||
|
|||
To connect to multiple Tor nodes, use: |
|||
## 4. Privacy recommendations |
|||
|
|||
./hushd -onion=127.0.0.1:9050 -addnode=hushbeef123.onion -dnsseed=0 -onlynet=onion |
|||
- Do not add anything but Hush ports to the onion service created in section 3. |
|||
If you run a web service too, create a new onion service for that. |
|||
Otherwise it is trivial to link them, which may reduce privacy. Onion |
|||
services created automatically (as in section 2) always have only one port |
|||
open. |
|||
|
@ -0,0 +1,122 @@ |
|||
// Copyright (c) 2009-2010 Satoshi Nakamoto
|
|||
// Copyright (c) 2009-2016 The Bitcoin Core developers
|
|||
// Copyright (c) 2016-2022 The Hush developers
|
|||
// Distributed under the GPLv3 software license, see the accompanying
|
|||
// file COPYING or https://www.gnu.org/licenses/gpl-3.0.en.html
|
|||
|
|||
#include "addrdb.h" |
|||
#include "addrman.h" |
|||
#include "chainparams.h" |
|||
#include "clientversion.h" |
|||
#include "fs.h" |
|||
#include "hash.h" |
|||
#include "random.h" |
|||
#include "streams.h" |
|||
#include "tinyformat.h" |
|||
#include "util.h" |
|||
|
|||
namespace { |
|||
|
|||
template <typename Stream, typename Data> |
|||
bool SerializeDB(Stream& stream, const Data& data) |
|||
{ |
|||
// Write and commit header, data
|
|||
try { |
|||
CHashWriter hasher(stream.GetType(), stream.GetVersion()); |
|||
stream << FLATDATA(Params().MessageStart()) << data; |
|||
hasher << FLATDATA(Params().MessageStart()) << data; |
|||
stream << hasher.GetHash(); |
|||
} catch (const std::exception& e) { |
|||
return error("%s: Serialize or I/O error - %s", __func__, e.what()); |
|||
} |
|||
|
|||
return true; |
|||
} |
|||
|
|||
template <typename Data> |
|||
bool SerializeFileDB(const std::string& prefix, const fs::path& path, const Data& data) |
|||
{ |
|||
// Generate random temporary filename
|
|||
unsigned short randv = 0; |
|||
GetRandBytes((unsigned char*)&randv, sizeof(randv)); |
|||
std::string tmpfn = strprintf("%s.%04x", prefix, randv); |
|||
|
|||
// open temp output file, and associate with CAutoFile
|
|||
fs::path pathTmp = GetDataDir() / tmpfn; |
|||
FILE *file = fsbridge::fopen(pathTmp, "wb"); |
|||
CAutoFile fileout(file, SER_DISK, CLIENT_VERSION); |
|||
if (fileout.IsNull()) |
|||
return error("%s: Failed to open file %s", __func__, pathTmp.string()); |
|||
|
|||
// Serialize
|
|||
if (!SerializeDB(fileout, data)) return false; |
|||
FileCommit(fileout.Get()); |
|||
fileout.fclose(); |
|||
|
|||
// replace existing file, if any, with new file
|
|||
if (!RenameOver(pathTmp, path)) |
|||
return error("%s: Rename-into-place failed", __func__); |
|||
|
|||
return true; |
|||
} |
|||
|
|||
template <typename Stream, typename Data> |
|||
bool DeserializeDB(Stream& stream, Data& data, bool fCheckSum = true) |
|||
{ |
|||
try { |
|||
CHashVerifier<Stream> verifier(&stream); |
|||
// de-serialize file header (network specific magic number) and ..
|
|||
unsigned char pchMsgTmp[4]; |
|||
verifier >> FLATDATA(pchMsgTmp); |
|||
// ... verify the network matches ours
|
|||
if (memcmp(pchMsgTmp, Params().MessageStart(), sizeof(pchMsgTmp))) |
|||
return error("%s: Invalid network magic number", __func__); |
|||
|
|||
// de-serialize data
|
|||
verifier >> data; |
|||
|
|||
// verify checksum
|
|||
if (fCheckSum) { |
|||
uint256 hashTmp; |
|||
stream >> hashTmp; |
|||
if (hashTmp != verifier.GetHash()) { |
|||
return error("%s: Checksum mismatch, data corrupted", __func__); |
|||
} |
|||
} |
|||
} |
|||
catch (const std::exception& e) { |
|||
return error("%s: Deserialize or I/O error - %s", __func__, e.what()); |
|||
} |
|||
|
|||
return true; |
|||
} |
|||
|
|||
template <typename Data> |
|||
bool DeserializeFileDB(const fs::path& path, Data& data) |
|||
{ |
|||
// open input file, and associate with CAutoFile
|
|||
FILE *file = fsbridge::fopen(path, "rb"); |
|||
CAutoFile filein(file, SER_DISK, CLIENT_VERSION); |
|||
if (filein.IsNull()) |
|||
return error("%s: Failed to open file %s", __func__, path.string()); |
|||
|
|||
return DeserializeDB(filein, data); |
|||
} |
|||
|
|||
} |
|||
|
|||
CBanDB::CBanDB() |
|||
{ |
|||
pathBanlist = GetDataDir() / "banlist.dat"; |
|||
} |
|||
|
|||
bool CBanDB::Write(const banmap_t& banSet) |
|||
{ |
|||
return SerializeFileDB("banlist", pathBanlist, banSet); |
|||
} |
|||
|
|||
bool CBanDB::Read(banmap_t& banSet) |
|||
{ |
|||
return DeserializeFileDB(pathBanlist, banSet); |
|||
} |
|||
|
@ -0,0 +1,90 @@ |
|||
// Copyright (c) 2009-2010 Satoshi Nakamoto
|
|||
// Copyright (c) 2009-2016 The Bitcoin Core developers
|
|||
// Distributed under the GPLv3 software license, see the accompanying
|
|||
// file COPYING or https://www.gnu.org/licenses/gpl-3.0.en.html
|
|||
|
|||
#ifndef HUSH_ADDRDB_H |
|||
#define HUSH_ADDRDB_H |
|||
|
|||
#include "fs.h" |
|||
#include "serialize.h" |
|||
|
|||
#include <string> |
|||
#include <map> |
|||
|
|||
class CSubNet; |
|||
class CAddrMan; |
|||
class CDataStream; |
|||
|
|||
typedef enum BanReason |
|||
{ |
|||
BanReasonUnknown = 0, |
|||
BanReasonNodeMisbehaving = 1, |
|||
BanReasonManuallyAdded = 2 |
|||
} BanReason; |
|||
|
|||
class CBanEntry |
|||
{ |
|||
public: |
|||
static const int CURRENT_VERSION=1; |
|||
int nVersion; |
|||
int64_t nCreateTime; |
|||
int64_t nBanUntil; |
|||
uint8_t banReason; |
|||
|
|||
CBanEntry() |
|||
{ |
|||
SetNull(); |
|||
} |
|||
|
|||
explicit CBanEntry(int64_t nCreateTimeIn) |
|||
{ |
|||
SetNull(); |
|||
nCreateTime = nCreateTimeIn; |
|||
} |
|||
|
|||
ADD_SERIALIZE_METHODS; |
|||
|
|||
template <typename Stream, typename Operation> |
|||
inline void SerializationOp(Stream& s, Operation ser_action) { |
|||
READWRITE(this->nVersion); |
|||
READWRITE(nCreateTime); |
|||
READWRITE(nBanUntil); |
|||
READWRITE(banReason); |
|||
} |
|||
|
|||
void SetNull() |
|||
{ |
|||
nVersion = CBanEntry::CURRENT_VERSION; |
|||
nCreateTime = 0; |
|||
nBanUntil = 0; |
|||
banReason = BanReasonUnknown; |
|||
} |
|||
|
|||
std::string banReasonToString() const |
|||
{ |
|||
switch (banReason) { |
|||
case BanReasonNodeMisbehaving: |
|||
return "node misbehaving"; |
|||
case BanReasonManuallyAdded: |
|||
return "manually added"; |
|||
default: |
|||
return "unknown"; |
|||
} |
|||
} |
|||
}; |
|||
|
|||
typedef std::map<CSubNet, CBanEntry> banmap_t; |
|||
|
|||
/** Access to the banlist database (banlist.dat) */ |
|||
class CBanDB |
|||
{ |
|||
private: |
|||
fs::path pathBanlist; |
|||
public: |
|||
CBanDB(); |
|||
bool Write(const banmap_t& banSet); |
|||
bool Read(banmap_t& banSet); |
|||
}; |
|||
|
|||
#endif // HUSH_ADDRDB_H
|
@ -0,0 +1,20 @@ |
|||
// Copyright (c) 2009-2010 Satoshi Nakamoto
|
|||
// Copyright (c) 2009-2020 The Bitcoin Core developers
|
|||
// Copyright (c) 2016-2022 The Hush developers
|
|||
// Distributed under the GPLv3 software license, see the accompanying
|
|||
// file COPYING or https://www.gnu.org/licenses/gpl-3.0.en.html
|
|||
|
|||
#ifndef HUSH_ATTRIBUTES_H |
|||
#define HUSH_ATTRIBUTES_H |
|||
|
|||
#if defined(__clang__) |
|||
# if __has_attribute(lifetimebound) |
|||
# define LIFETIMEBOUND [[clang::lifetimebound]] |
|||
# else |
|||
# define LIFETIMEBOUND |
|||
# endif |
|||
#else |
|||
# define LIFETIMEBOUND |
|||
#endif |
|||
|
|||
#endif // HUSH_ATTRIBUTES_H
|
@ -1,37 +1,20 @@ |
|||
// Copyright (c) 2016-2022 The Hush developers
|
|||
/******************************************************************************
|
|||
* Copyright © 2014-2019 The SuperNET Developers. * |
|||
* * |
|||
* See the AUTHORS, DEVELOPER-AGREEMENT and LICENSE files at * |
|||
* the top-level directory of this distribution for the individual copyright * |
|||
* holder information and the developer policies on copyright and licensing. * |
|||
* * |
|||
* Unless otherwise agreed in a custom licensing agreement, no part of the * |
|||
* SuperNET software, including this file may be copied, modified, propagated * |
|||
* or distributed except according to the terms contained in the LICENSE file * |
|||
* * |
|||
* Removal or modification of this copyright notice is prohibited. * |
|||
* * |
|||
******************************************************************************/ |
|||
|
|||
// Distributed under the GPLv3 software license, see the accompanying
|
|||
// file COPYING or https://www.gnu.org/licenses/gpl-3.0.en.html
|
|||
// THIS FILE IS AUTOGENERATED, DO NOT MODIFY DIRECTLY
|
|||
// Instead, run: ./contrib/seeds/generate-seeds.py contrib/seeds
|
|||
#ifndef HUSH_CHAINPARAMSSEEDS_H |
|||
#define HUSH_CHAINPARAMSSEEDS_H |
|||
/**
|
|||
* List of fixed seed nodes for the bitcoin network |
|||
* AUTOGENERATED by contrib/seeds/generate-seeds.py |
|||
* |
|||
* Each line contains a 16-byte IPv6 address and a port. |
|||
* IPv4 as well as onion addresses are wrapped inside a IPv6 address accordingly. |
|||
*/ |
|||
static SeedSpec6 pnSeed6_main[] = { |
|||
{{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0xb9,0x19,0x30,0xec}, 27485}, |
|||
{{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0xb9,0x19,0x30,0xec}, 27487}, |
|||
{{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0xb9,0x40,0x69,0x6f}, 27485}, |
|||
{{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0xb9,0x40,0x69,0x6f}, 27487}, |
|||
{{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0xb9,0x19,0x30,0x48}, 27485}, |
|||
{{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0xb9,0x19,0x30,0x48}, 27487} |
|||
// List of fixed seed nodes for the Hush network
|
|||
// Each line contains a BIP155 serialized address.
|
|||
//
|
|||
static const uint8_t chainparams_seed_main[] = { |
|||
0x01,0x04,0xb9,0xf1,0x3d,0x2b,0x00,0x00, // 185.241.61.43
|
|||
0x01,0x04,0x89,0x4a,0x04,0xc6,0x00,0x00, // 137.74.4.198
|
|||
0x01,0x04,0x95,0x1c,0x66,0xdb,0x00,0x00, // 149.28.102.219
|
|||
}; |
|||
|
|||
static SeedSpec6 pnSeed6_test[] = { |
|||
static const uint8_t chainparams_seed_test[] = { |
|||
0x01,0x04,0x01,0x02,0x03,0x04,0x00,0x00, // 1.2.3.4
|
|||
}; |
|||
#endif // HUSH_CHAINPARAMSSEEDS_H
|
|||
|
@ -0,0 +1,162 @@ |
|||
// Copyright (c) 2020 The Bitcoin Core developers
|
|||
// Copyright (c) 2016-2022 The Hush developers
|
|||
// Distributed under the GPLv3 software license, see the accompanying
|
|||
// file COPYING or https://www.gnu.org/licenses/gpl-3.0.en.html
|
|||
|
|||
// Based on https://github.com/mjosaarinen/tiny_sha3/blob/master/sha3.c
|
|||
// by Markku-Juhani O. Saarinen <mjos@iki.fi>
|
|||
|
|||
#include "crypto/sha3.h" |
|||
#include "crypto/common.h" |
|||
#include "span.h" |
|||
|
|||
#include <algorithm> |
|||
#include <array> // For std::begin and std::end. |
|||
|
|||
#include <stdint.h> |
|||
|
|||
// Internal implementation code.
|
|||
namespace |
|||
{ |
|||
uint64_t Rotl(uint64_t x, int n) { return (x << n) | (x >> (64 - n)); } |
|||
} // namespace
|
|||
|
|||
void KeccakF(uint64_t (&st)[25]) |
|||
{ |
|||
static constexpr uint64_t RNDC[24] = { |
|||
0x0000000000000001, 0x0000000000008082, 0x800000000000808a, 0x8000000080008000, |
|||
0x000000000000808b, 0x0000000080000001, 0x8000000080008081, 0x8000000000008009, |
|||
0x000000000000008a, 0x0000000000000088, 0x0000000080008009, 0x000000008000000a, |
|||
0x000000008000808b, 0x800000000000008b, 0x8000000000008089, 0x8000000000008003, |
|||
0x8000000000008002, 0x8000000000000080, 0x000000000000800a, 0x800000008000000a, |
|||
0x8000000080008081, 0x8000000000008080, 0x0000000080000001, 0x8000000080008008 |
|||
}; |
|||
static constexpr int ROUNDS = 24; |
|||
|
|||
for (int round = 0; round < ROUNDS; ++round) { |
|||
uint64_t bc0, bc1, bc2, bc3, bc4, t; |
|||
|
|||
// Theta
|
|||
bc0 = st[0] ^ st[5] ^ st[10] ^ st[15] ^ st[20]; |
|||
bc1 = st[1] ^ st[6] ^ st[11] ^ st[16] ^ st[21]; |
|||
bc2 = st[2] ^ st[7] ^ st[12] ^ st[17] ^ st[22]; |
|||
bc3 = st[3] ^ st[8] ^ st[13] ^ st[18] ^ st[23]; |
|||
bc4 = st[4] ^ st[9] ^ st[14] ^ st[19] ^ st[24]; |
|||
t = bc4 ^ Rotl(bc1, 1); st[0] ^= t; st[5] ^= t; st[10] ^= t; st[15] ^= t; st[20] ^= t; |
|||
t = bc0 ^ Rotl(bc2, 1); st[1] ^= t; st[6] ^= t; st[11] ^= t; st[16] ^= t; st[21] ^= t; |
|||
t = bc1 ^ Rotl(bc3, 1); st[2] ^= t; st[7] ^= t; st[12] ^= t; st[17] ^= t; st[22] ^= t; |
|||
t = bc2 ^ Rotl(bc4, 1); st[3] ^= t; st[8] ^= t; st[13] ^= t; st[18] ^= t; st[23] ^= t; |
|||
t = bc3 ^ Rotl(bc0, 1); st[4] ^= t; st[9] ^= t; st[14] ^= t; st[19] ^= t; st[24] ^= t; |
|||
|
|||
// Rho Pi
|
|||
t = st[1]; |
|||
bc0 = st[10]; st[10] = Rotl(t, 1); t = bc0; |
|||
bc0 = st[7]; st[7] = Rotl(t, 3); t = bc0; |
|||
bc0 = st[11]; st[11] = Rotl(t, 6); t = bc0; |
|||
bc0 = st[17]; st[17] = Rotl(t, 10); t = bc0; |
|||
bc0 = st[18]; st[18] = Rotl(t, 15); t = bc0; |
|||
bc0 = st[3]; st[3] = Rotl(t, 21); t = bc0; |
|||
bc0 = st[5]; st[5] = Rotl(t, 28); t = bc0; |
|||
bc0 = st[16]; st[16] = Rotl(t, 36); t = bc0; |
|||
bc0 = st[8]; st[8] = Rotl(t, 45); t = bc0; |
|||
bc0 = st[21]; st[21] = Rotl(t, 55); t = bc0; |
|||
bc0 = st[24]; st[24] = Rotl(t, 2); t = bc0; |
|||
bc0 = st[4]; st[4] = Rotl(t, 14); t = bc0; |
|||
bc0 = st[15]; st[15] = Rotl(t, 27); t = bc0; |
|||
bc0 = st[23]; st[23] = Rotl(t, 41); t = bc0; |
|||
bc0 = st[19]; st[19] = Rotl(t, 56); t = bc0; |
|||
bc0 = st[13]; st[13] = Rotl(t, 8); t = bc0; |
|||
bc0 = st[12]; st[12] = Rotl(t, 25); t = bc0; |
|||
bc0 = st[2]; st[2] = Rotl(t, 43); t = bc0; |
|||
bc0 = st[20]; st[20] = Rotl(t, 62); t = bc0; |
|||
bc0 = st[14]; st[14] = Rotl(t, 18); t = bc0; |
|||
bc0 = st[22]; st[22] = Rotl(t, 39); t = bc0; |
|||
bc0 = st[9]; st[9] = Rotl(t, 61); t = bc0; |
|||
bc0 = st[6]; st[6] = Rotl(t, 20); t = bc0; |
|||
st[1] = Rotl(t, 44); |
|||
|
|||
// Chi Iota
|
|||
bc0 = st[0]; bc1 = st[1]; bc2 = st[2]; bc3 = st[3]; bc4 = st[4]; |
|||
st[0] = bc0 ^ (~bc1 & bc2) ^ RNDC[round]; |
|||
st[1] = bc1 ^ (~bc2 & bc3); |
|||
st[2] = bc2 ^ (~bc3 & bc4); |
|||
st[3] = bc3 ^ (~bc4 & bc0); |
|||
st[4] = bc4 ^ (~bc0 & bc1); |
|||
bc0 = st[5]; bc1 = st[6]; bc2 = st[7]; bc3 = st[8]; bc4 = st[9]; |
|||
st[5] = bc0 ^ (~bc1 & bc2); |
|||
st[6] = bc1 ^ (~bc2 & bc3); |
|||
st[7] = bc2 ^ (~bc3 & bc4); |
|||
st[8] = bc3 ^ (~bc4 & bc0); |
|||
st[9] = bc4 ^ (~bc0 & bc1); |
|||
bc0 = st[10]; bc1 = st[11]; bc2 = st[12]; bc3 = st[13]; bc4 = st[14]; |
|||
st[10] = bc0 ^ (~bc1 & bc2); |
|||
st[11] = bc1 ^ (~bc2 & bc3); |
|||
st[12] = bc2 ^ (~bc3 & bc4); |
|||
st[13] = bc3 ^ (~bc4 & bc0); |
|||
st[14] = bc4 ^ (~bc0 & bc1); |
|||
bc0 = st[15]; bc1 = st[16]; bc2 = st[17]; bc3 = st[18]; bc4 = st[19]; |
|||
st[15] = bc0 ^ (~bc1 & bc2); |
|||
st[16] = bc1 ^ (~bc2 & bc3); |
|||
st[17] = bc2 ^ (~bc3 & bc4); |
|||
st[18] = bc3 ^ (~bc4 & bc0); |
|||
st[19] = bc4 ^ (~bc0 & bc1); |
|||
bc0 = st[20]; bc1 = st[21]; bc2 = st[22]; bc3 = st[23]; bc4 = st[24]; |
|||
st[20] = bc0 ^ (~bc1 & bc2); |
|||
st[21] = bc1 ^ (~bc2 & bc3); |
|||
st[22] = bc2 ^ (~bc3 & bc4); |
|||
st[23] = bc3 ^ (~bc4 & bc0); |
|||
st[24] = bc4 ^ (~bc0 & bc1); |
|||
} |
|||
} |
|||
|
|||
SHA3_256_& SHA3_256_::Write(Span<const unsigned char> data) |
|||
{ |
|||
if (m_bufsize && m_bufsize + data.size() >= sizeof(m_buffer)) { |
|||
// Fill the buffer and process it.
|
|||
std::copy(data.begin(), data.begin() + sizeof(m_buffer) - m_bufsize, m_buffer + m_bufsize); |
|||
data = data.subspan(sizeof(m_buffer) - m_bufsize); |
|||
m_state[m_pos++] ^= ReadLE64(m_buffer); |
|||
m_bufsize = 0; |
|||
if (m_pos == RATE_BUFFERS) { |
|||
KeccakF(m_state); |
|||
m_pos = 0; |
|||
} |
|||
} |
|||
while (data.size() >= sizeof(m_buffer)) { |
|||
// Process chunks directly from the buffer.
|
|||
m_state[m_pos++] ^= ReadLE64(data.data()); |
|||
data = data.subspan(8); |
|||
if (m_pos == RATE_BUFFERS) { |
|||
KeccakF(m_state); |
|||
m_pos = 0; |
|||
} |
|||
} |
|||
if (data.size()) { |
|||
// Keep the remainder in the buffer.
|
|||
std::copy(data.begin(), data.end(), m_buffer + m_bufsize); |
|||
m_bufsize += data.size(); |
|||
} |
|||
return *this; |
|||
} |
|||
|
|||
SHA3_256_& SHA3_256_::Finalize(Span<unsigned char> output) |
|||
{ |
|||
assert(output.size() == OUTPUT_SIZE); |
|||
std::fill(m_buffer + m_bufsize, m_buffer + sizeof(m_buffer), 0); |
|||
m_buffer[m_bufsize] ^= 0x06; |
|||
m_state[m_pos] ^= ReadLE64(m_buffer); |
|||
m_state[RATE_BUFFERS - 1] ^= 0x8000000000000000; |
|||
KeccakF(m_state); |
|||
for (unsigned i = 0; i < 4; ++i) { |
|||
WriteLE64(output.data() + 8 * i, m_state[i]); |
|||
} |
|||
return *this; |
|||
} |
|||
|
|||
SHA3_256_& SHA3_256_::Reset() |
|||
{ |
|||
m_bufsize = 0; |
|||
m_pos = 0; |
|||
std::fill(std::begin(m_state), std::end(m_state), 0); |
|||
return *this; |
|||
} |
@ -0,0 +1,42 @@ |
|||
// Copyright (c) 2020 The Bitcoin Core developers
|
|||
// Copyright (c) 2016-2022 The Hush developers
|
|||
// Distributed under the GPLv3 software license, see the accompanying
|
|||
// file COPYING or https://www.gnu.org/licenses/gpl-3.0.en.html
|
|||
|
|||
#ifndef HUSH_CRYPTO_SHA3_H |
|||
#define HUSH_CRYPTO_SHA3_H |
|||
|
|||
#include "span.h" |
|||
|
|||
#include <stdint.h> |
|||
#include <stdlib.h> |
|||
|
|||
//! The Keccak-f[1600] transform.
|
|||
void KeccakF(uint64_t (&st)[25]); |
|||
|
|||
class SHA3_256_ |
|||
{ |
|||
private: |
|||
uint64_t m_state[25] = {0}; |
|||
unsigned char m_buffer[8]; |
|||
unsigned m_bufsize = 0; |
|||
unsigned m_pos = 0; |
|||
|
|||
//! Sponge rate in bits.
|
|||
static constexpr unsigned RATE_BITS = 1088; |
|||
|
|||
//! Sponge rate expressed as a multiple of the buffer size.
|
|||
static constexpr unsigned RATE_BUFFERS = RATE_BITS / (8 * sizeof(m_buffer)); |
|||
|
|||
static_assert(RATE_BITS % (8 * sizeof(m_buffer)) == 0, "Rate must be a multiple of 8 bytes"); |
|||
|
|||
public: |
|||
static constexpr size_t OUTPUT_SIZE = 32; |
|||
|
|||
SHA3_256_() {} |
|||
SHA3_256_& Write(Span<const unsigned char> data); |
|||
SHA3_256_& Finalize(Span<unsigned char> output); |
|||
SHA3_256_& Reset(); |
|||
}; |
|||
|
|||
#endif // HUSH_CRYPTO_SHA3_H
|
@ -0,0 +1,441 @@ |
|||
// Copyright (c) 2020-2020 The Bitcoin Core developers
|
|||
// Copyright (c) 2016-2022 The Hush developers
|
|||
// Distributed under the GPLv3 software license, see the accompanying
|
|||
// file COPYING or https://www.gnu.org/licenses/gpl-3.0.en.html
|
|||
|
|||
#include <init.h> |
|||
#include <chainparams.h> |
|||
#include <compat.h> |
|||
#include <compat/endian.h> |
|||
#include <crypto/sha256.h> |
|||
#include <fs.h> |
|||
#include <i2p.h> |
|||
#include <netbase.h> |
|||
#include <random.h> |
|||
#include <util/strencodings.h> |
|||
#include <tinyformat.h> |
|||
#include <util/readwritefile.h> |
|||
#include <util/sock.h> |
|||
#include <util/spanparsing.h> |
|||
#include <util.h> |
|||
|
|||
#include <chrono> |
|||
#include <memory> |
|||
#include <stdexcept> |
|||
#include <string> |
|||
|
|||
namespace i2p { |
|||
|
|||
/**
|
|||
* Swap Standard Base64 <-> I2P Base64. |
|||
* Standard Base64 uses `+` and `/` as last two characters of its alphabet. |
|||
* I2P Base64 uses `-` and `~` respectively. |
|||
* So it is easy to detect in which one is the input and convert to the other. |
|||
* @param[in] from Input to convert. |
|||
* @return converted `from` |
|||
*/ |
|||
static std::string SwapBase64(const std::string& from) |
|||
{ |
|||
std::string to; |
|||
to.resize(from.size()); |
|||
for (size_t i = 0; i < from.size(); ++i) { |
|||
switch (from[i]) { |
|||
case '-': |
|||
to[i] = '+'; |
|||
break; |
|||
case '~': |
|||
to[i] = '/'; |
|||
break; |
|||
case '+': |
|||
to[i] = '-'; |
|||
break; |
|||
case '/': |
|||
to[i] = '~'; |
|||
break; |
|||
default: |
|||
to[i] = from[i]; |
|||
break; |
|||
} |
|||
} |
|||
return to; |
|||
} |
|||
|
|||
/**
|
|||
* Decode an I2P-style Base64 string. |
|||
* @param[in] i2p_b64 I2P-style Base64 string. |
|||
* @return decoded `i2p_b64` |
|||
* @throw std::runtime_error if decoding fails |
|||
*/ |
|||
static Binary DecodeI2PBase64(const std::string& i2p_b64) |
|||
{ |
|||
const std::string& std_b64 = SwapBase64(i2p_b64); |
|||
bool invalid; |
|||
Binary decoded = DecodeBase64(std_b64.c_str(), &invalid); |
|||
if (invalid) { |
|||
throw std::runtime_error(strprintf("Cannot decode Base64: \"%s\"", i2p_b64)); |
|||
} |
|||
return decoded; |
|||
} |
|||
|
|||
/**
|
|||
* Derive the .b32.i2p address of an I2P destination (binary). |
|||
* @param[in] dest I2P destination. |
|||
* @return the address that corresponds to `dest` |
|||
* @throw std::runtime_error if conversion fails |
|||
*/ |
|||
static CNetAddr DestBinToAddr(const Binary& dest) |
|||
{ |
|||
CSHA256 hasher; |
|||
hasher.Write(dest.data(), dest.size()); |
|||
unsigned char hash[CSHA256::OUTPUT_SIZE]; |
|||
hasher.Finalize(hash); |
|||
|
|||
CNetAddr addr; |
|||
const std::string addr_str = EncodeBase32(hash, false) + ".b32.i2p"; |
|||
if (!addr.SetSpecial(addr_str)) { |
|||
throw std::runtime_error(strprintf("Cannot parse I2P address: \"%s\"", addr_str)); |
|||
} |
|||
|
|||
return addr; |
|||
} |
|||
|
|||
/**
|
|||
* Derive the .b32.i2p address of an I2P destination (I2P-style Base64). |
|||
* @param[in] dest I2P destination. |
|||
* @return the address that corresponds to `dest` |
|||
* @throw std::runtime_error if conversion fails |
|||
*/ |
|||
static CNetAddr DestB64ToAddr(const std::string& dest) |
|||
{ |
|||
const Binary& decoded = DecodeI2PBase64(dest); |
|||
return DestBinToAddr(decoded); |
|||
} |
|||
|
|||
namespace sam { |
|||
|
|||
Session::Session(const fs::path& private_key_file, |
|||
const CService& control_host) |
|||
: m_private_key_file(private_key_file), m_control_host(control_host), |
|||
m_control_sock(std::unique_ptr<Sock>(new Sock(INVALID_SOCKET))) |
|||
{ |
|||
} |
|||
|
|||
Session::~Session() |
|||
{ |
|||
} |
|||
|
|||
bool Session::Check() |
|||
{ |
|||
try { |
|||
LOCK(cs_i2p); |
|||
CreateIfNotCreatedAlready(); |
|||
return true; |
|||
} catch (const std::runtime_error& e) { |
|||
LogPrint("i2p","I2P: Error Checking Session: %s\n", e.what()); |
|||
CheckControlSock(); |
|||
} |
|||
return false; |
|||
} |
|||
|
|||
bool Session::Listen(Connection& conn) |
|||
{ |
|||
try { |
|||
LOCK(cs_i2p); |
|||
CreateIfNotCreatedAlready(); |
|||
conn.me = m_my_addr; |
|||
conn.sock = StreamAccept(); |
|||
return true; |
|||
} catch (const std::runtime_error& e) { |
|||
LogPrint("i2p","I2P: Error listening: %s\n", e.what()); |
|||
CheckControlSock(); |
|||
} |
|||
return false; |
|||
} |
|||
|
|||
bool Session::Accept(Connection& conn) |
|||
{ |
|||
try { |
|||
while (true) { |
|||
|
|||
// boost::this_thread::interruption_point();
|
|||
if (ShutdownRequested()) { |
|||
Disconnect(); |
|||
return false; |
|||
} |
|||
|
|||
|
|||
Sock::Event occurred; |
|||
if (!conn.sock->Wait(std::chrono::milliseconds{MAX_WAIT_FOR_IO}, Sock::RECV, &occurred)) { |
|||
throw std::runtime_error("wait on socket failed"); |
|||
} |
|||
|
|||
if ((occurred & Sock::RECV) == 0) { |
|||
// Timeout, no incoming connections within MAX_WAIT_FOR_IO.
|
|||
continue; |
|||
} |
|||
|
|||
const std::string& peer_dest = |
|||
conn.sock->RecvUntilTerminator('\n', std::chrono::milliseconds{MAX_WAIT_FOR_IO}, MAX_MSG_SIZE); |
|||
|
|||
if (ShutdownRequested()) { |
|||
Disconnect(); |
|||
return false; |
|||
} |
|||
|
|||
conn.peer = CService(DestB64ToAddr(peer_dest), Params().GetDefaultPort()); |
|||
return true; |
|||
} |
|||
} catch (const std::runtime_error& e) { |
|||
LogPrint("i2p","I2P: Error accepting: %s\n", e.what()); |
|||
CheckControlSock(); |
|||
} |
|||
return false; |
|||
} |
|||
|
|||
bool Session::Connect(const CService& to, Connection& conn, bool& proxy_error) |
|||
{ |
|||
proxy_error = true; |
|||
|
|||
std::string session_id; |
|||
std::unique_ptr<Sock> sock; |
|||
conn.peer = to; |
|||
|
|||
try { |
|||
{ |
|||
LOCK(cs_i2p); |
|||
CreateIfNotCreatedAlready(); |
|||
session_id = m_session_id; |
|||
conn.me = m_my_addr; |
|||
sock = Hello(); |
|||
} |
|||
|
|||
const Reply& lookup_reply = |
|||
SendRequestAndGetReply(*sock, strprintf("NAMING LOOKUP NAME=%s", to.ToStringIP())); |
|||
|
|||
const std::string& dest = lookup_reply.Get("VALUE"); |
|||
|
|||
const Reply& connect_reply = SendRequestAndGetReply( |
|||
*sock, strprintf("STREAM CONNECT ID=%s DESTINATION=%s SILENT=false", session_id, dest), |
|||
false); |
|||
|
|||
const std::string& result = connect_reply.Get("RESULT"); |
|||
|
|||
if (result == "OK") { |
|||
conn.sock = std::move(sock); |
|||
return true; |
|||
} |
|||
|
|||
if (result == "INVALID_ID") { |
|||
LOCK(cs_i2p); |
|||
Disconnect(); |
|||
throw std::runtime_error("Invalid session id"); |
|||
} |
|||
|
|||
if (result == "CANT_REACH_PEER" || result == "TIMEOUT" || "KEY_NOT_FOUND") { |
|||
proxy_error = false; |
|||
} |
|||
|
|||
throw std::runtime_error(strprintf("\"%s\"", connect_reply.full)); |
|||
} catch (const std::runtime_error& e) { |
|||
LogPrint("i2p","I2P: Error connecting to %s: %s\n", to.ToString(), e.what()); |
|||
CheckControlSock(); |
|||
return false; |
|||
} |
|||
} |
|||
|
|||
// Private methods
|
|||
|
|||
std::string Session::Reply::Get(const std::string& key) const |
|||
{ |
|||
const auto& pos = keys.find(key); |
|||
if (pos == keys.end() || !pos->second.has_value()) { |
|||
throw std::runtime_error( |
|||
strprintf("Missing %s= in the reply to \"%s\": \"%s\"", key, request, full)); |
|||
} |
|||
return pos->second.value(); |
|||
} |
|||
|
|||
Session::Reply Session::SendRequestAndGetReply(const Sock& sock, |
|||
const std::string& request, |
|||
bool check_result_ok) const |
|||
{ |
|||
sock.SendComplete(request + "\n", std::chrono::milliseconds{MAX_WAIT_FOR_IO}); |
|||
|
|||
Reply reply; |
|||
|
|||
// Don't log the full "SESSION CREATE ..." because it contains our private key.
|
|||
reply.request = request.substr(0, 14) == "SESSION CREATE" ? "SESSION CREATE ..." : request; |
|||
|
|||
// It could take a few minutes for the I2P router to reply as it is querying the I2P network
|
|||
// (when doing name lookup, for example).
|
|||
|
|||
reply.full = sock.RecvUntilTerminator('\n', std::chrono::minutes{3}, MAX_MSG_SIZE); |
|||
|
|||
for (const auto& kv : spanparsing::Split(reply.full, ' ')) { |
|||
const auto& pos = std::find(kv.begin(), kv.end(), '='); |
|||
if (pos != kv.end()) { |
|||
reply.keys.emplace(std::string{kv.begin(), pos}, std::string{pos + 1, kv.end()}); |
|||
} else { |
|||
reply.keys.emplace(std::string{kv.begin(), kv.end()}, boost::none); |
|||
} |
|||
} |
|||
|
|||
LogPrint("i2p","I2P: Handshake reply %s\n", reply.full); |
|||
|
|||
if (check_result_ok && reply.Get("RESULT") != "OK") { |
|||
if (!ShutdownRequested()) { |
|||
throw std::runtime_error(strprintf("Unexpected reply to \"%s\": \"%s\"", request, reply.full)); |
|||
} |
|||
} |
|||
|
|||
return reply; |
|||
} |
|||
|
|||
std::unique_ptr<Sock> Session::Hello() const |
|||
{ |
|||
auto sock = CreateSock(m_control_host); |
|||
|
|||
if (!sock) { |
|||
throw std::runtime_error("Cannot create socket"); |
|||
} |
|||
|
|||
if (!ConnectSocketDirectly(m_control_host, *sock, nConnectTimeout)) { |
|||
throw std::runtime_error(strprintf("Cannot connect to %s", m_control_host.ToString())); |
|||
} |
|||
|
|||
SendRequestAndGetReply(*sock, "HELLO VERSION MIN=3.1 MAX=3.1"); |
|||
|
|||
return sock; |
|||
} |
|||
|
|||
void Session::CheckControlSock() |
|||
{ |
|||
LOCK(cs_i2p); |
|||
|
|||
std::string errmsg; |
|||
if (!m_control_sock->IsConnected(errmsg)) { |
|||
LogPrint("i2p","I2P: Control socket error: %s\n", errmsg); |
|||
Disconnect(); |
|||
} |
|||
} |
|||
|
|||
void Session::DestGenerate(const Sock& sock) |
|||
{ |
|||
// https://geti2p.net/spec/common-structures#key-certificates
|
|||
// "7" or "EdDSA_SHA512_Ed25519" - "Recent Router Identities and Destinations".
|
|||
// Use "7" because i2pd <2.24.0 does not recognize the textual form.
|
|||
const Reply& reply = SendRequestAndGetReply(sock, "DEST GENERATE SIGNATURE_TYPE=7", false); |
|||
|
|||
m_private_key = DecodeI2PBase64(reply.Get("PRIV")); |
|||
} |
|||
|
|||
void Session::GenerateAndSavePrivateKey(const Sock& sock) |
|||
{ |
|||
DestGenerate(sock); |
|||
|
|||
// umask is set to 077 in init.cpp, which is ok (unless -sysperms is given)
|
|||
if (!WriteBinaryFile(m_private_key_file, |
|||
std::string(m_private_key.begin(), m_private_key.end()))) { |
|||
throw std::runtime_error( |
|||
strprintf("Cannot save I2P private key to %s", m_private_key_file)); |
|||
} |
|||
} |
|||
|
|||
Binary Session::MyDestination() const |
|||
{ |
|||
// From https://geti2p.net/spec/common-structures#destination:
|
|||
// "They are 387 bytes plus the certificate length specified at bytes 385-386, which may be
|
|||
// non-zero"
|
|||
static constexpr size_t DEST_LEN_BASE = 387; |
|||
static constexpr size_t CERT_LEN_POS = 385; |
|||
|
|||
uint16_t cert_len; |
|||
memcpy(&cert_len, &m_private_key.at(CERT_LEN_POS), sizeof(cert_len)); |
|||
cert_len = be16toh(cert_len); |
|||
|
|||
const size_t dest_len = DEST_LEN_BASE + cert_len; |
|||
|
|||
return Binary{m_private_key.begin(), m_private_key.begin() + dest_len}; |
|||
} |
|||
|
|||
void Session::CreateIfNotCreatedAlready() |
|||
{ |
|||
LOCK(cs_i2p); |
|||
|
|||
std::string errmsg; |
|||
if (m_control_sock->IsConnected(errmsg)) { |
|||
return; |
|||
} |
|||
|
|||
LogPrint("i2p","I2P: Creating SAM session with %s\n", m_control_host.ToString()); |
|||
|
|||
auto sock = Hello(); |
|||
|
|||
const std::pair<bool,std::string> i2pRead = ReadBinaryFile(m_private_key_file); |
|||
if (i2pRead.first) { |
|||
m_private_key.assign(i2pRead.second.begin(), i2pRead.second.end()); |
|||
} else { |
|||
GenerateAndSavePrivateKey(*sock); |
|||
} |
|||
|
|||
const std::string& session_id = GetRandHash().GetHex().substr(0, 10); // full is an overkill, too verbose in the logs
|
|||
const std::string& private_key_b64 = SwapBase64(EncodeBase64(m_private_key)); |
|||
|
|||
SendRequestAndGetReply(*sock, strprintf("SESSION CREATE STYLE=STREAM ID=%s DESTINATION=%s", |
|||
session_id, private_key_b64)); |
|||
|
|||
m_my_addr = CService(DestBinToAddr(MyDestination()), Params().GetDefaultPort()); |
|||
m_session_id = session_id; |
|||
m_control_sock = std::move(sock); |
|||
|
|||
LogPrint("i2p","I2P: SAM session created: session id=%s, my address=%s\n", m_session_id, |
|||
m_my_addr.ToString()); |
|||
} |
|||
|
|||
std::unique_ptr<Sock> Session::StreamAccept() |
|||
{ |
|||
auto sock = Hello(); |
|||
|
|||
const Reply& reply = SendRequestAndGetReply( |
|||
*sock, strprintf("STREAM ACCEPT ID=%s SILENT=false", m_session_id), false); |
|||
|
|||
const std::string& result = reply.Get("RESULT"); |
|||
|
|||
if (result == "OK") { |
|||
return sock; |
|||
} |
|||
|
|||
if (result == "INVALID_ID") { |
|||
// If our session id is invalid, then force session re-creation on next usage.
|
|||
Disconnect(); |
|||
} |
|||
|
|||
throw std::runtime_error(strprintf("\"%s\"", reply.full)); |
|||
} |
|||
|
|||
void Session::Disconnect() |
|||
{ |
|||
LOCK(cs_i2p); |
|||
try |
|||
{ |
|||
if (m_control_sock->Get() != INVALID_SOCKET) { |
|||
if (m_session_id.empty()) { |
|||
LogPrint("i2p","I2P: Destroying incomplete session\n"); |
|||
} else { |
|||
LogPrint("i2p","I2P: Destroying session %s\n", m_session_id); |
|||
} |
|||
} |
|||
m_control_sock->Reset(); |
|||
m_session_id.clear(); |
|||
} |
|||
catch(std::bad_alloc&) |
|||
{ |
|||
// when the node is shutting down, the call above might use invalid memory resulting in a
|
|||
// std::bad_alloc exception when instantiating internal objs for handling log category
|
|||
LogPrintf("(node is probably shutting down) Destroying session=%d\n", m_session_id); |
|||
} |
|||
|
|||
|
|||
} |
|||
} // namespace sam
|
|||
} // namespace i2p
|
@ -0,0 +1,256 @@ |
|||
// Copyright (c) 2020-2020 The Bitcoin Core developers
|
|||
// Copyright (c) 2016-2022 The Hush developers
|
|||
// Distributed under the GPLv3 software license, see the accompanying
|
|||
// file COPYING or https://www.gnu.org/licenses/gpl-3.0.en.html
|
|||
|
|||
#ifndef HUSH_I2P_H |
|||
#define HUSH_I2P_H |
|||
|
|||
#include <compat.h> |
|||
#include <fs.h> |
|||
#include <netaddress.h> |
|||
#include <sync.h> |
|||
#include <util/sock.h> |
|||
|
|||
#include <memory> |
|||
#include <optional> |
|||
#include <string> |
|||
#include <unordered_map> |
|||
#include <vector> |
|||
|
|||
namespace i2p { |
|||
|
|||
/**
|
|||
* Binary data. |
|||
*/ |
|||
using Binary = std::vector<uint8_t>; |
|||
|
|||
/**
|
|||
* An established connection with another peer. |
|||
*/ |
|||
struct Connection { |
|||
/** Connected socket. */ |
|||
std::unique_ptr<Sock> sock; |
|||
|
|||
/** Our I2P address. */ |
|||
CService me; |
|||
|
|||
/** The peer's I2P address. */ |
|||
CService peer; |
|||
}; |
|||
|
|||
namespace sam { |
|||
|
|||
/**
|
|||
* The maximum size of an incoming message from the I2P SAM proxy (in bytes). |
|||
* Used to avoid a runaway proxy from sending us an "unlimited" amount of data without a terminator. |
|||
* The longest known message is ~1400 bytes, so this is high enough not to be triggered during |
|||
* normal operation, yet low enough to avoid a malicious proxy from filling our memory. |
|||
*/ |
|||
static constexpr size_t MAX_MSG_SIZE{65536}; |
|||
|
|||
/**
|
|||
* I2P SAM session. |
|||
*/ |
|||
class Session |
|||
{ |
|||
public: |
|||
/**
|
|||
* Construct a session. This will not initiate any IO, the session will be lazily created |
|||
* later when first used. |
|||
* @param[in] private_key_file Path to a private key file. If the file does not exist then the |
|||
* private key will be generated and saved into the file. |
|||
* @param[in] control_host Location of the SAM proxy. |
|||
*/ |
|||
Session(const fs::path& private_key_file, |
|||
const CService& control_host); |
|||
|
|||
/**
|
|||
* Destroy the session, closing the internally used sockets. The sockets that have been |
|||
* returned by `Accept()` or `Connect()` will not be closed, but they will be closed by |
|||
* the SAM proxy because the session is destroyed. So they will return an error next time |
|||
* we try to read or write to them. |
|||
*/ |
|||
~Session(); |
|||
|
|||
/**
|
|||
* Check the control sock and restart if needed |
|||
*/ |
|||
bool Check(); |
|||
|
|||
/**
|
|||
* Start listening for an incoming connection. |
|||
* @param[out] conn Upon successful completion the `sock` and `me` members will be set |
|||
* to the listening socket and address. |
|||
* @return true on success |
|||
*/ |
|||
bool Listen(Connection& conn); |
|||
|
|||
/**
|
|||
* Wait for and accept a new incoming connection. |
|||
* @param[in,out] conn The `sock` member is used for waiting and accepting. Upon successful |
|||
* completion the `peer` member will be set to the address of the incoming peer. |
|||
* @return true on success |
|||
*/ |
|||
bool Accept(Connection& conn); |
|||
|
|||
/**
|
|||
* Connect to an I2P peer. |
|||
* @param[in] to Peer to connect to. |
|||
* @param[out] conn Established connection. Only set if `true` is returned. |
|||
* @param[out] proxy_error If an error occurs due to proxy or general network failure, then |
|||
* this is set to `true`. If an error occurs due to unreachable peer (likely peer is down), then |
|||
* it is set to `false`. Only set if `false` is returned. |
|||
* @return true on success |
|||
*/ |
|||
bool Connect(const CService& to, Connection& conn, bool& proxy_error); |
|||
|
|||
protected: |
|||
|
|||
CCriticalSection cs_i2p; |
|||
|
|||
private: |
|||
/**
|
|||
* A reply from the SAM proxy. |
|||
*/ |
|||
struct Reply { |
|||
/**
|
|||
* Full, unparsed reply. |
|||
*/ |
|||
std::string full; |
|||
|
|||
/**
|
|||
* Request, used for detailed error reporting. |
|||
*/ |
|||
std::string request; |
|||
|
|||
/**
|
|||
* A map of keywords from the parsed reply. |
|||
* For example, if the reply is "A=X B C=YZ", then the map will be |
|||
* keys["A"] == "X" |
|||
* keys["B"] == (empty std::optional) |
|||
* keys["C"] == "YZ" |
|||
*/ |
|||
std::unordered_map<std::string, boost::optional<std::string>> keys; |
|||
|
|||
/**
|
|||
* Get the value of a given key. |
|||
* For example if the reply is "A=X B" then: |
|||
* Value("A") -> "X" |
|||
* Value("B") -> throws |
|||
* Value("C") -> throws |
|||
* @param[in] key Key whose value to retrieve |
|||
* @returns the key's value |
|||
* @throws std::runtime_error if the key is not present or if it has no value |
|||
*/ |
|||
std::string Get(const std::string& key) const; |
|||
}; |
|||
|
|||
/**
|
|||
* Send request and get a reply from the SAM proxy. |
|||
* @param[in] sock A socket that is connected to the SAM proxy. |
|||
* @param[in] request Raw request to send, a newline terminator is appended to it. |
|||
* @param[in] check_result_ok If true then after receiving the reply a check is made |
|||
* whether it contains "RESULT=OK" and an exception is thrown if it does not. |
|||
* @throws std::runtime_error if an error occurs |
|||
*/ |
|||
Reply SendRequestAndGetReply(const Sock& sock, |
|||
const std::string& request, |
|||
bool check_result_ok = true) const; |
|||
|
|||
/**
|
|||
* Open a new connection to the SAM proxy. |
|||
* @return a connected socket |
|||
* @throws std::runtime_error if an error occurs |
|||
*/ |
|||
std::unique_ptr<Sock> Hello() const EXCLUSIVE_LOCKS_REQUIRED(cs_i2p); |
|||
|
|||
/**
|
|||
* Check the control socket for errors and possibly disconnect. |
|||
*/ |
|||
void CheckControlSock(); |
|||
|
|||
/**
|
|||
* Generate a new destination with the SAM proxy and set `m_private_key` to it. |
|||
* @param[in] sock Socket to use for talking to the SAM proxy. |
|||
* @throws std::runtime_error if an error occurs |
|||
*/ |
|||
void DestGenerate(const Sock& sock) EXCLUSIVE_LOCKS_REQUIRED(cs_i2p); |
|||
|
|||
/**
|
|||
* Generate a new destination with the SAM proxy, set `m_private_key` to it and save |
|||
* it on disk to `m_private_key_file`. |
|||
* @param[in] sock Socket to use for talking to the SAM proxy. |
|||
* @throws std::runtime_error if an error occurs |
|||
*/ |
|||
void GenerateAndSavePrivateKey(const Sock& sock) EXCLUSIVE_LOCKS_REQUIRED(cs_i2p); |
|||
|
|||
/**
|
|||
* Derive own destination from `m_private_key`. |
|||
* @see https://geti2p.net/spec/common-structures#destination
|
|||
* @return an I2P destination |
|||
*/ |
|||
Binary MyDestination() const EXCLUSIVE_LOCKS_REQUIRED(cs_i2p); |
|||
|
|||
/**
|
|||
* Create the session if not already created. Reads the private key file and connects to the |
|||
* SAM proxy. |
|||
* @throws std::runtime_error if an error occurs |
|||
*/ |
|||
void CreateIfNotCreatedAlready() EXCLUSIVE_LOCKS_REQUIRED(cs_i2p); |
|||
|
|||
/**
|
|||
* Open a new connection to the SAM proxy and issue "STREAM ACCEPT" request using the existing |
|||
* session id. |
|||
* @return the idle socket that is waiting for a peer to connect to us |
|||
* @throws std::runtime_error if an error occurs |
|||
*/ |
|||
std::unique_ptr<Sock> StreamAccept() EXCLUSIVE_LOCKS_REQUIRED(cs_i2p); |
|||
|
|||
/**
|
|||
* Destroy the session, closing the internally used sockets. |
|||
*/ |
|||
void Disconnect() EXCLUSIVE_LOCKS_REQUIRED(cs_i2p); |
|||
|
|||
/**
|
|||
* The name of the file where this peer's private key is stored (in binary). |
|||
*/ |
|||
const fs::path m_private_key_file; |
|||
|
|||
/**
|
|||
* The host and port of the SAM control service. |
|||
*/ |
|||
const CService m_control_host; |
|||
|
|||
/**
|
|||
* The private key of this peer. |
|||
* @see The reply to the "DEST GENERATE" command in https://geti2p.net/en/docs/api/samv3
|
|||
*/ |
|||
Binary m_private_key GUARDED_BY(cs_i2p); |
|||
|
|||
/**
|
|||
* SAM control socket. |
|||
* Used to connect to the I2P SAM service and create a session |
|||
* ("SESSION CREATE"). With the established session id we later open |
|||
* other connections to the SAM service to accept incoming I2P |
|||
* connections and make outgoing ones. |
|||
* See https://geti2p.net/en/docs/api/samv3
|
|||
*/ |
|||
std::unique_ptr<Sock> m_control_sock GUARDED_BY(cs_i2p); |
|||
|
|||
/**
|
|||
* Our .b32.i2p address. |
|||
* Derived from `m_private_key`. |
|||
*/ |
|||
CService m_my_addr GUARDED_BY(cs_i2p); |
|||
|
|||
/**
|
|||
* SAM session id. |
|||
*/ |
|||
std::string m_session_id GUARDED_BY(cs_i2p); |
|||
}; |
|||
|
|||
} // namespace sam
|
|||
} // namespace i2p
|
|||
|
|||
#endif /* HUSH_I2P_H */ |
File diff suppressed because it is too large
File diff suppressed because it is too large
@ -0,0 +1,534 @@ |
|||
// Copyright (c) 2009-2015 The Bitcoin Core developers
|
|||
// Copyright (c) 2016-2022 The Hush developers
|
|||
// Distributed under the GPLv3 software license, see the accompanying
|
|||
// file COPYING or https://www.gnu.org/licenses/gpl-3.0.en.html
|
|||
|
|||
#ifndef HUSH_NETADDRESS_H |
|||
#define HUSH_NETADDRESS_H |
|||
|
|||
#if defined(HAVE_CONFIG_H) |
|||
#include "config/bitcoin-config.h" |
|||
#endif |
|||
|
|||
#include "attributes.h" |
|||
#include "compat.h" |
|||
#include "prevector.h" |
|||
#include "serialize.h" |
|||
#include "tinyformat.h" |
|||
#include "util/strencodings.h" |
|||
#include "util/string.h" |
|||
|
|||
#include <array> |
|||
#include <cstdint> |
|||
#include <ios> |
|||
#include <string> |
|||
#include <vector> |
|||
|
|||
/**
|
|||
* A flag that is ORed into the protocol version to designate that addresses |
|||
* should be serialized in (unserialized from) v2 format (BIP155). |
|||
* Make sure that this does not collide with any of the values in `version.h` |
|||
* or with `SERIALIZE_TRANSACTION_NO_WITNESS`. |
|||
*/ |
|||
static const int ADDRV2_FORMAT = 0x20000000; |
|||
|
|||
/**
|
|||
* A network type. |
|||
* @note An address may belong to more than one network, for example `10.0.0.1` |
|||
* belongs to both `NET_UNROUTABLE` and `NET_IPV4`. |
|||
* Keep these sequential starting from 0 and `NET_MAX` as the last entry. |
|||
* We have loops like `for (int i = 0; i < NET_MAX; i++)` that expect to iterate |
|||
* over all enum values and also `GetExtNetwork()` "extends" this enum by |
|||
* introducing standalone constants starting from `NET_MAX`. |
|||
*/ |
|||
enum Network |
|||
{ |
|||
/// Addresses from these networks are not publicly routable on the global Internet.
|
|||
NET_UNROUTABLE = 0, |
|||
|
|||
/// IPv4
|
|||
NET_IPV4, |
|||
|
|||
/// IPv6
|
|||
NET_IPV6, |
|||
|
|||
/// TOR (v2 or v3)
|
|||
NET_ONION, |
|||
|
|||
/// I2P
|
|||
NET_I2P, |
|||
|
|||
/// CJDNS
|
|||
NET_CJDNS, |
|||
|
|||
/// A set of addresses that represent the hash of a string or FQDN. We use
|
|||
/// them in CAddrMan to keep track of which DNS seeds were used.
|
|||
NET_INTERNAL, |
|||
|
|||
/// Dummy value to indicate the number of NET_* constants.
|
|||
NET_MAX, |
|||
}; |
|||
|
|||
/// Prefix of an IPv6 address when it contains an embedded IPv4 address.
|
|||
/// Used when (un)serializing addresses in ADDRv1 format (pre-BIP155).
|
|||
static const std::array<uint8_t, 12> IPV4_IN_IPV6_PREFIX{ |
|||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF |
|||
}; |
|||
|
|||
/// Prefix of an IPv6 address when it contains an embedded TORv2 address.
|
|||
/// Used when (un)serializing addresses in ADDRv1 format (pre-BIP155).
|
|||
/// Such dummy IPv6 addresses are guaranteed to not be publicly routable as they
|
|||
/// fall under RFC4193's fc00::/7 subnet allocated to unique-local addresses.
|
|||
static const std::array<uint8_t, 6> TORV2_IN_IPV6_PREFIX{ |
|||
0xFD, 0x87, 0xD8, 0x7E, 0xEB, 0x43 |
|||
}; |
|||
|
|||
/// Prefix of an IPv6 address when it contains an embedded "internal" address.
|
|||
/// Used when (un)serializing addresses in ADDRv1 format (pre-BIP155).
|
|||
/// The prefix comes from 0xFD + SHA256("bitcoin")[0:5].
|
|||
/// Such dummy IPv6 addresses are guaranteed to not be publicly routable as they
|
|||
/// fall under RFC4193's fc00::/7 subnet allocated to unique-local addresses.
|
|||
static const std::array<uint8_t, 6> INTERNAL_IN_IPV6_PREFIX{ |
|||
0xFD, 0x6B, 0x88, 0xC0, 0x87, 0x24 // 0xFD + sha256("bitcoin")[0:5].
|
|||
}; |
|||
|
|||
/// Size of IPv4 address (in bytes).
|
|||
static constexpr uint64_t ADDR_IPV4_SIZE = 4; |
|||
|
|||
/// Size of IPv6 address (in bytes).
|
|||
static constexpr uint64_t ADDR_IPV6_SIZE = 16; |
|||
|
|||
/// Size of TORv2 address (in bytes).
|
|||
static constexpr uint64_t ADDR_TORV2_SIZE = 10; |
|||
|
|||
/// Size of TORv3 address (in bytes). This is the length of just the address
|
|||
/// as used in BIP155, without the checksum and the version byte.
|
|||
static constexpr uint64_t ADDR_TORV3_SIZE = 32; |
|||
|
|||
/// Size of I2P address (in bytes).
|
|||
static constexpr uint64_t ADDR_I2P_SIZE = 32; |
|||
|
|||
/// Size of CJDNS address (in bytes).
|
|||
static constexpr uint64_t ADDR_CJDNS_SIZE = 16; |
|||
|
|||
/// Size of "internal" (NET_INTERNAL) address (in bytes).
|
|||
static constexpr uint64_t ADDR_INTERNAL_SIZE = 10; |
|||
|
|||
/**
|
|||
* Network address. |
|||
*/ |
|||
class CNetAddr |
|||
{ |
|||
protected: |
|||
/**
|
|||
* Raw representation of the network address. |
|||
* In network byte order (big endian) for IPv4 and IPv6. |
|||
*/ |
|||
prevector<ADDR_IPV6_SIZE, uint8_t> m_addr{ADDR_IPV6_SIZE, 0x0}; |
|||
|
|||
/**
|
|||
* Network to which this address belongs. |
|||
*/ |
|||
Network m_net{NET_IPV6}; |
|||
|
|||
uint32_t scopeId{0}; // for scoped/link-local ipv6 addresses
|
|||
|
|||
public: |
|||
CNetAddr(); |
|||
explicit CNetAddr(const struct in_addr& ipv4Addr); |
|||
void SetIP(const CNetAddr& ip); |
|||
|
|||
private: |
|||
/**
|
|||
* Set from a legacy IPv6 address. |
|||
* Legacy IPv6 address may be a normal IPv6 address, or another address |
|||
* (e.g. IPv4) disguised as IPv6. This encoding is used in the legacy |
|||
* `addr` encoding. |
|||
*/ |
|||
void SetLegacyIPv6(Span<const uint8_t> ipv6); |
|||
|
|||
public: |
|||
/** check whether a given address is in a network we can probably connect to */ |
|||
bool IsReachableNetwork(); |
|||
bool SetInternal(const std::string& name); |
|||
|
|||
bool SetSpecial(const std::string &strName); // for Tor addresses
|
|||
|
|||
bool IsBindAny() const; // INADDR_ANY equivalent
|
|||
bool IsIPv4() const; // IPv4 mapped address (::FFFF:0:0/96, 0.0.0.0/0)
|
|||
bool IsIPv6() const; // IPv6 address (not mapped IPv4, not Tor)
|
|||
bool IsRFC1918() const; // IPv4 private networks (10.0.0.0/8, 192.168.0.0/16, 172.16.0.0/12)
|
|||
bool IsRFC2544() const; // IPv4 inter-network communcations (192.18.0.0/15)
|
|||
bool IsRFC6598() const; // IPv4 ISP-level NAT (100.64.0.0/10)
|
|||
bool IsRFC5737() const; // IPv4 documentation addresses (192.0.2.0/24, 198.51.100.0/24, 203.0.113.0/24)
|
|||
bool IsRFC3849() const; // IPv6 documentation address (2001:0DB8::/32)
|
|||
bool IsRFC3927() const; // IPv4 autoconfig (169.254.0.0/16)
|
|||
bool IsRFC3964() const; // IPv6 6to4 tunnelling (2002::/16)
|
|||
bool IsRFC4193() const; // IPv6 unique local (FC00::/7)
|
|||
bool IsRFC4380() const; // IPv6 Teredo tunnelling (2001::/32)
|
|||
bool IsRFC4843() const; // IPv6 ORCHID (deprecated) (2001:10::/28)
|
|||
bool IsRFC7343() const; // IPv6 ORCHIDv2 (2001:20::/28)
|
|||
bool IsRFC4862() const; // IPv6 autoconfig (FE80::/64)
|
|||
bool IsRFC6052() const; // IPv6 well-known prefix for IPv4-embedded address (64:FF9B::/96)
|
|||
bool IsRFC6145() const; // IPv6 IPv4-translated address (::FFFF:0:0:0/96) (actually defined in RFC2765)
|
|||
bool IsHeNet() const; // IPv6 Hurricane Electric - https://he.net (2001:0470::/36)
|
|||
bool IsTor() const; |
|||
bool IsI2P() const; |
|||
bool IsCJDNS() const; |
|||
bool IsLocal() const; |
|||
bool IsRoutable() const; |
|||
bool IsInternal() const; |
|||
bool IsValid() const; |
|||
|
|||
/**
|
|||
* Check if the current object can be serialized in pre-ADDRv2/BIP155 format. |
|||
*/ |
|||
bool IsAddrV1Compatible() const; |
|||
|
|||
enum Network GetNetwork() const; |
|||
std::string ToString() const; |
|||
std::string ToStringIP() const; |
|||
uint64_t GetHash() const; |
|||
bool GetInAddr(struct in_addr* pipv4Addr) const; |
|||
uint32_t GetNetClass() const; |
|||
|
|||
//! For IPv4, mapped IPv4, SIIT translated IPv4, Teredo, 6to4 tunneled addresses, return the relevant IPv4 address as a uint32.
|
|||
uint32_t GetLinkedIPv4() const; |
|||
//! Whether this address has a linked IPv4 address (see GetLinkedIPv4()).
|
|||
bool HasLinkedIPv4() const; |
|||
|
|||
// The AS on the BGP path to the node we use to diversify
|
|||
// peers in AddrMan bucketing based on the AS infrastructure.
|
|||
// The ip->AS mapping depends on how asmap is constructed.
|
|||
uint32_t GetMappedAS(const std::vector<bool> &asmap) const; |
|||
|
|||
std::vector<unsigned char> GetGroup(const std::vector<bool> &asmap) const; |
|||
std::vector<unsigned char> GetAddrBytes() const; |
|||
int GetReachabilityFrom(const CNetAddr *paddrPartner = nullptr) const; |
|||
|
|||
CNetAddr(const struct in6_addr& pipv6Addr, const uint32_t scope = 0); |
|||
bool GetIn6Addr(struct in6_addr* pipv6Addr) const; |
|||
|
|||
friend bool operator==(const CNetAddr& a, const CNetAddr& b); |
|||
friend bool operator!=(const CNetAddr& a, const CNetAddr& b) { return !(a == b); } |
|||
friend bool operator<(const CNetAddr& a, const CNetAddr& b); |
|||
|
|||
/**
|
|||
* Serialize to a stream. |
|||
*/ |
|||
template <typename Stream> |
|||
void Serialize(Stream& s) const |
|||
{ |
|||
if (s.GetVersion() & ADDRV2_FORMAT) { |
|||
SerializeV2Stream(s); |
|||
} else { |
|||
SerializeV1Stream(s); |
|||
} |
|||
} |
|||
|
|||
/**
|
|||
* Unserialize from a stream. |
|||
*/ |
|||
template <typename Stream> |
|||
void Unserialize(Stream& s) |
|||
{ |
|||
if (s.GetVersion() & ADDRV2_FORMAT) { |
|||
UnserializeV2Stream(s); |
|||
} else { |
|||
UnserializeV1Stream(s); |
|||
} |
|||
} |
|||
|
|||
friend class CSubNet; |
|||
|
|||
private: |
|||
/**
|
|||
* Parse a Tor address and set this object to it. |
|||
* @param[in] addr Address to parse, must be a valid C string, for example |
|||
* pg6mmjiyjmcrsslvykfwnntlaru7p5svn6y2ymmju6nubxndf4pscryd.onion. |
|||
* @returns Whether the operation was successful. |
|||
* @see CNetAddr::IsTor() |
|||
*/ |
|||
bool SetTor(const std::string& addr); |
|||
|
|||
/**
|
|||
* Parse an I2P address and set this object to it. |
|||
* @param[in] addr Address to parse, must be a valid C string, for example |
|||
* ukeu3k5oycgaauneqgtnvselmt4yemvoilkln7jpvamvfx7dnkdq.b32.i2p. |
|||
* @returns Whether the operation was successful. |
|||
* @see CNetAddr::IsI2P() |
|||
*/ |
|||
bool SetI2P(const std::string& addr); |
|||
/**
|
|||
* BIP155 network ids recognized by this software. |
|||
*/ |
|||
enum BIP155Network : uint8_t { |
|||
IPV4 = 1, |
|||
IPV6 = 2, |
|||
TORV2 = 3, |
|||
TORV3 = 4, |
|||
I2P = 5, |
|||
CJDNS = 6, |
|||
}; |
|||
|
|||
/**
|
|||
* Size of CNetAddr when serialized as ADDRv1 (pre-BIP155) (in bytes). |
|||
*/ |
|||
static constexpr uint64_t V1_SERIALIZATION_SIZE = ADDR_IPV6_SIZE; |
|||
|
|||
/**
|
|||
* Maximum size of an address as defined in BIP155 (in bytes). |
|||
* This is only the size of the address, not the entire CNetAddr object |
|||
* when serialized. |
|||
*/ |
|||
static constexpr uint64_t MAX_ADDRV2_SIZE = 512; |
|||
|
|||
/**
|
|||
* Get the BIP155 network id of this address. |
|||
* Must not be called for IsInternal() objects. |
|||
* @returns BIP155 network id |
|||
*/ |
|||
BIP155Network GetBIP155Network() const; |
|||
|
|||
/**
|
|||
* Set `m_net` from the provided BIP155 network id and size after validation. |
|||
* @retval true the network was recognized, is valid and `m_net` was set |
|||
* @retval false not recognised (from future?) and should be silently ignored |
|||
* @throws std::ios_base::failure if the network is one of the BIP155 founding |
|||
* networks (id 1..6) with wrong address size. |
|||
*/ |
|||
bool SetNetFromBIP155Network(uint8_t possible_bip155_net, uint64_t address_size); |
|||
|
|||
/**
|
|||
* Serialize in pre-ADDRv2/BIP155 format to an array. |
|||
*/ |
|||
void SerializeV1Array(uint8_t (&arr)[V1_SERIALIZATION_SIZE]) const |
|||
{ |
|||
uint64_t prefix_size; |
|||
|
|||
switch (m_net) { |
|||
case NET_IPV6: |
|||
assert(m_addr.size() == sizeof(arr)); |
|||
memcpy(arr, m_addr.data(), m_addr.size()); |
|||
return; |
|||
case NET_IPV4: |
|||
prefix_size = sizeof(IPV4_IN_IPV6_PREFIX); |
|||
assert(prefix_size + m_addr.size() == sizeof(arr)); |
|||
memcpy(arr, IPV4_IN_IPV6_PREFIX.data(), prefix_size); |
|||
memcpy(arr + prefix_size, m_addr.data(), m_addr.size()); |
|||
return; |
|||
case NET_ONION: |
|||
if (m_addr.size() == ADDR_TORV3_SIZE) { |
|||
break; |
|||
} |
|||
prefix_size = sizeof(TORV2_IN_IPV6_PREFIX); |
|||
assert(prefix_size + m_addr.size() == sizeof(arr)); |
|||
memcpy(arr, TORV2_IN_IPV6_PREFIX.data(), prefix_size); |
|||
memcpy(arr + prefix_size, m_addr.data(), m_addr.size()); |
|||
return; |
|||
case NET_INTERNAL: |
|||
prefix_size = sizeof(INTERNAL_IN_IPV6_PREFIX); |
|||
assert(prefix_size + m_addr.size() == sizeof(arr)); |
|||
memcpy(arr, INTERNAL_IN_IPV6_PREFIX.data(), prefix_size); |
|||
memcpy(arr + prefix_size, m_addr.data(), m_addr.size()); |
|||
return; |
|||
case NET_I2P: |
|||
break; |
|||
case NET_CJDNS: |
|||
break; |
|||
case NET_UNROUTABLE: |
|||
case NET_MAX: |
|||
assert(false); |
|||
} // no default case, so the compiler can warn about missing cases
|
|||
|
|||
// Serialize TORv3, I2P and CJDNS as all-zeros.
|
|||
memset(arr, 0x0, V1_SERIALIZATION_SIZE); |
|||
} |
|||
|
|||
/**
|
|||
* Serialize in pre-ADDRv2/BIP155 format to a stream. |
|||
*/ |
|||
template <typename Stream> |
|||
void SerializeV1Stream(Stream& s) const |
|||
{ |
|||
uint8_t serialized[V1_SERIALIZATION_SIZE]; |
|||
|
|||
SerializeV1Array(serialized); |
|||
|
|||
s << serialized; |
|||
} |
|||
|
|||
/**
|
|||
* Serialize as ADDRv2 / BIP155. |
|||
*/ |
|||
template <typename Stream> |
|||
void SerializeV2Stream(Stream& s) const |
|||
{ |
|||
if (IsInternal()) { |
|||
// Serialize NET_INTERNAL as embedded in IPv6. We need to
|
|||
// serialize such addresses from addrman.
|
|||
s << static_cast<uint8_t>(BIP155Network::IPV6); |
|||
s << COMPACTSIZE(ADDR_IPV6_SIZE); |
|||
SerializeV1Stream(s); |
|||
return; |
|||
} |
|||
|
|||
s << static_cast<uint8_t>(GetBIP155Network()); |
|||
s << m_addr; |
|||
} |
|||
|
|||
/**
|
|||
* Unserialize from a pre-ADDRv2/BIP155 format from an array. |
|||
*/ |
|||
void UnserializeV1Array(uint8_t (&arr)[V1_SERIALIZATION_SIZE]) |
|||
{ |
|||
// Use SetLegacyIPv6() so that m_net is set correctly. For example
|
|||
// ::FFFF:0102:0304 should be set as m_net=NET_IPV4 (1.2.3.4).
|
|||
SetLegacyIPv6(arr); |
|||
} |
|||
|
|||
/**
|
|||
* Unserialize from a pre-ADDRv2/BIP155 format from a stream. |
|||
*/ |
|||
template <typename Stream> |
|||
void UnserializeV1Stream(Stream& s) |
|||
{ |
|||
uint8_t serialized[V1_SERIALIZATION_SIZE]; |
|||
|
|||
s >> serialized; |
|||
|
|||
UnserializeV1Array(serialized); |
|||
} |
|||
|
|||
/**
|
|||
* Unserialize from a ADDRv2 / BIP155 format. |
|||
*/ |
|||
template <typename Stream> |
|||
void UnserializeV2Stream(Stream& s) |
|||
{ |
|||
uint8_t bip155_net; |
|||
s >> bip155_net; |
|||
|
|||
uint64_t address_size; |
|||
s >> COMPACTSIZE(address_size); |
|||
|
|||
if (address_size > MAX_ADDRV2_SIZE) { |
|||
throw std::ios_base::failure(strprintf( |
|||
"Address too long: %u > %u", address_size, MAX_ADDRV2_SIZE)); |
|||
} |
|||
|
|||
scopeId = 0; |
|||
|
|||
if (SetNetFromBIP155Network(bip155_net, address_size)) { |
|||
m_addr.resize(address_size); |
|||
s >> MakeSpan(m_addr); |
|||
|
|||
if (m_net != NET_IPV6) { |
|||
return; |
|||
} |
|||
|
|||
// Do some special checks on IPv6 addresses.
|
|||
|
|||
// Recognize NET_INTERNAL embedded in IPv6, such addresses are not
|
|||
// gossiped but could be coming from addrman, when unserializing from
|
|||
// disk.
|
|||
if (HasPrefix(m_addr, INTERNAL_IN_IPV6_PREFIX)) { |
|||
m_net = NET_INTERNAL; |
|||
memmove(m_addr.data(), m_addr.data() + INTERNAL_IN_IPV6_PREFIX.size(), |
|||
ADDR_INTERNAL_SIZE); |
|||
m_addr.resize(ADDR_INTERNAL_SIZE); |
|||
return; |
|||
} |
|||
|
|||
if (!HasPrefix(m_addr, IPV4_IN_IPV6_PREFIX) && |
|||
!HasPrefix(m_addr, TORV2_IN_IPV6_PREFIX)) { |
|||
return; |
|||
} |
|||
|
|||
// IPv4 and TORv2 are not supposed to be embedded in IPv6 (like in V1
|
|||
// encoding). Unserialize as !IsValid(), thus ignoring them.
|
|||
} else { |
|||
// If we receive an unknown BIP155 network id (from the future?) then
|
|||
// ignore the address - unserialize as !IsValid().
|
|||
s.ignore(address_size); |
|||
} |
|||
|
|||
// Mimic a default-constructed CNetAddr object which is !IsValid() and thus
|
|||
// will not be gossiped, but continue reading next addresses from the stream.
|
|||
m_net = NET_IPV6; |
|||
m_addr.assign(ADDR_IPV6_SIZE, 0x0); |
|||
} |
|||
}; |
|||
|
|||
class CSubNet |
|||
{ |
|||
protected: |
|||
/// Network (base) address
|
|||
CNetAddr network; |
|||
/// Netmask, in network byte order
|
|||
uint8_t netmask[16]; |
|||
/// Is this value valid? (only used to signal parse errors)
|
|||
bool valid; |
|||
|
|||
public: |
|||
CSubNet(); |
|||
CSubNet(const CNetAddr& addr, uint8_t mask); |
|||
CSubNet(const CNetAddr& addr, const CNetAddr& mask); |
|||
|
|||
//constructor for single ip subnet (<ipv4>/32 or <ipv6>/128)
|
|||
explicit CSubNet(const CNetAddr& addr); |
|||
|
|||
bool Match(const CNetAddr &addr) const; |
|||
|
|||
std::string ToString() const; |
|||
bool IsValid() const; |
|||
|
|||
friend bool operator==(const CSubNet& a, const CSubNet& b); |
|||
friend bool operator!=(const CSubNet& a, const CSubNet& b) { return !(a == b); } |
|||
friend bool operator<(const CSubNet& a, const CSubNet& b); |
|||
|
|||
ADD_SERIALIZE_METHODS; |
|||
|
|||
template <typename Stream, typename Operation> |
|||
inline void SerializationOp(Stream& s, Operation ser_action) { |
|||
READWRITE(network); |
|||
READWRITE(FLATDATA(netmask)); |
|||
READWRITE(FLATDATA(valid)); |
|||
} |
|||
}; |
|||
|
|||
/** A combination of a network address (CNetAddr) and a (TCP) port */ |
|||
class CService : public CNetAddr |
|||
{ |
|||
protected: |
|||
unsigned short port; // host order
|
|||
|
|||
public: |
|||
CService(); |
|||
CService(const CNetAddr& ip, unsigned short port); |
|||
CService(const struct in_addr& ipv4Addr, unsigned short port); |
|||
explicit CService(const struct sockaddr_in& addr); |
|||
unsigned short GetPort() const; |
|||
bool GetSockAddr(struct sockaddr* paddr, socklen_t *addrlen) const; |
|||
bool SetSockAddr(const struct sockaddr* paddr); |
|||
friend bool operator==(const CService& a, const CService& b); |
|||
friend bool operator!=(const CService& a, const CService& b) { return !(a == b); } |
|||
friend bool operator<(const CService& a, const CService& b); |
|||
std::vector<unsigned char> GetKey() const; |
|||
std::string ToString() const; |
|||
std::string ToStringPort() const; |
|||
std::string ToStringIPPort() const; |
|||
|
|||
CService(const struct in6_addr& ipv6Addr, unsigned short port); |
|||
CService(const struct sockaddr_in6& addr); |
|||
|
|||
SERIALIZE_METHODS(CService, obj) |
|||
{ |
|||
READWRITEAS(CNetAddr, obj); |
|||
READ_WRITE(Using<BigEndianFormatter<2>>(obj.port)); |
|||
} |
|||
}; |
|||
|
|||
bool SanityCheckASMap(const std::vector<bool>& asmap); |
|||
|
|||
#endif // HUSH_NETADDRESS_H
|
File diff suppressed because it is too large
@ -0,0 +1,37 @@ |
|||
// Copyright (c) 2009-2010 Satoshi Nakamoto
|
|||
// Copyright (c) 2009-2020 The Bitcoin Core developers
|
|||
// Copyright (c) 2016-2022 The Hush developers
|
|||
// Distributed under the GPLv3 software license, see the accompanying
|
|||
// file COPYING or https://www.gnu.org/licenses/gpl-3.0.en.html
|
|||
|
|||
#ifndef HUSH_NETMESSAGEMAKER_H |
|||
#define HUSH_NETMESSAGEMAKER_H |
|||
|
|||
#include <net.h> |
|||
#include <serialize.h> |
|||
|
|||
class CNetMsgMaker |
|||
{ |
|||
public: |
|||
explicit CNetMsgMaker(int nVersionIn) : nVersion(nVersionIn){} |
|||
|
|||
template <typename... Args> |
|||
CSerializedNetMsg Make(int nFlags, std::string msg_type, Args&&... args) const |
|||
{ |
|||
CSerializedNetMsg msg; |
|||
msg.m_type = std::move(msg_type); |
|||
CVectorWriter{ SER_NETWORK, nFlags | nVersion, msg.data, 0, std::forward<Args>(args)... }; |
|||
return msg; |
|||
} |
|||
|
|||
template <typename... Args> |
|||
CSerializedNetMsg Make(std::string msg_type, Args&&... args) const |
|||
{ |
|||
return Make(0, std::move(msg_type), std::forward<Args>(args)...); |
|||
} |
|||
|
|||
private: |
|||
const int nVersion; |
|||
}; |
|||
|
|||
#endif // HUSH_NETMESSAGEMAKER_H
|
@ -0,0 +1,252 @@ |
|||
// Copyright (c) 2018-2020 The Bitcoin Core developers
|
|||
// Copyright (c) 2016-2022 The Hush developers
|
|||
// Distributed under the GPLv3 software license, see the accompanying
|
|||
// file COPYING or https://www.gnu.org/licenses/gpl-3.0.en.html
|
|||
|
|||
#ifndef BITCOIN_SPAN_H |
|||
#define BITCOIN_SPAN_H |
|||
|
|||
#include <type_traits> |
|||
#include <cstddef> |
|||
#include <algorithm> |
|||
#include <assert.h> |
|||
|
|||
#ifdef DEBUG |
|||
#define CONSTEXPR_IF_NOT_DEBUG |
|||
#define ASSERT_IF_DEBUG(x) assert((x)) |
|||
#else |
|||
#define CONSTEXPR_IF_NOT_DEBUG constexpr |
|||
#define ASSERT_IF_DEBUG(x) |
|||
#endif |
|||
|
|||
#if defined(__clang__) |
|||
#if __has_attribute(lifetimebound) |
|||
#define SPAN_ATTR_LIFETIMEBOUND [[clang::lifetimebound]] |
|||
#else |
|||
#define SPAN_ATTR_LIFETIMEBOUND |
|||
#endif |
|||
#else |
|||
#define SPAN_ATTR_LIFETIMEBOUND |
|||
#endif |
|||
|
|||
/** A Span is an object that can refer to a contiguous sequence of objects.
|
|||
* |
|||
* It implements a subset of C++20's std::span. |
|||
* |
|||
* Things to be aware of when writing code that deals with Spans: |
|||
* |
|||
* - Similar to references themselves, Spans are subject to reference lifetime |
|||
* issues. The user is responsible for making sure the objects pointed to by |
|||
* a Span live as long as the Span is used. For example: |
|||
* |
|||
* std::vector<int> vec{1,2,3,4}; |
|||
* Span<int> sp(vec); |
|||
* vec.push_back(5); |
|||
* printf("%i\n", sp.front()); // UB!
|
|||
* |
|||
* may exhibit undefined behavior, as increasing the size of a vector may |
|||
* invalidate references. |
|||
* |
|||
* - One particular pitfall is that Spans can be constructed from temporaries, |
|||
* but this is unsafe when the Span is stored in a variable, outliving the |
|||
* temporary. For example, this will compile, but exhibits undefined behavior: |
|||
* |
|||
* Span<const int> sp(std::vector<int>{1, 2, 3}); |
|||
* printf("%i\n", sp.front()); // UB!
|
|||
* |
|||
* The lifetime of the vector ends when the statement it is created in ends. |
|||
* Thus the Span is left with a dangling reference, and using it is undefined. |
|||
* |
|||
* - Due to Span's automatic creation from range-like objects (arrays, and data |
|||
* types that expose a data() and size() member function), functions that |
|||
* accept a Span as input parameter can be called with any compatible |
|||
* range-like object. For example, this works: |
|||
* |
|||
* void Foo(Span<const int> arg); |
|||
* |
|||
* Foo(std::vector<int>{1, 2, 3}); // Works
|
|||
* |
|||
* This is very useful in cases where a function truly does not care about the |
|||
* container, and only about having exactly a range of elements. However it |
|||
* may also be surprising to see automatic conversions in this case. |
|||
* |
|||
* When a function accepts a Span with a mutable element type, it will not |
|||
* accept temporaries; only variables or other references. For example: |
|||
* |
|||
* void FooMut(Span<int> arg); |
|||
* |
|||
* FooMut(std::vector<int>{1, 2, 3}); // Does not compile
|
|||
* std::vector<int> baz{1, 2, 3}; |
|||
* FooMut(baz); // Works
|
|||
* |
|||
* This is similar to how functions that take (non-const) lvalue references |
|||
* as input cannot accept temporaries. This does not work either: |
|||
* |
|||
* void FooVec(std::vector<int>& arg); |
|||
* FooVec(std::vector<int>{1, 2, 3}); // Does not compile
|
|||
* |
|||
* The idea is that if a function accepts a mutable reference, a meaningful |
|||
* result will be present in that variable after the call. Passing a temporary |
|||
* is useless in that context. |
|||
*/ |
|||
template<typename C> |
|||
class Span |
|||
{ |
|||
C* m_data; |
|||
std::size_t m_size; |
|||
|
|||
template <class T> |
|||
struct is_Span_int : public std::false_type {}; |
|||
template <class T> |
|||
struct is_Span_int<Span<T>> : public std::true_type {}; |
|||
template <class T> |
|||
struct is_Span : public is_Span_int<typename std::remove_cv<T>::type>{}; |
|||
|
|||
|
|||
public: |
|||
constexpr Span() noexcept : m_data(nullptr), m_size(0) {} |
|||
|
|||
/** Construct a span from a begin pointer and a size.
|
|||
* |
|||
* This implements a subset of the iterator-based std::span constructor in C++20, |
|||
* which is hard to implement without std::address_of. |
|||
*/ |
|||
template <typename T, typename std::enable_if<std::is_convertible<T (*)[], C (*)[]>::value, int>::type = 0> |
|||
constexpr Span(T* begin, std::size_t size) noexcept : m_data(begin), m_size(size) {} |
|||
|
|||
/** Construct a span from a begin and end pointer.
|
|||
* |
|||
* This implements a subset of the iterator-based std::span constructor in C++20, |
|||
* which is hard to implement without std::address_of. |
|||
*/ |
|||
template <typename T, typename std::enable_if<std::is_convertible<T (*)[], C (*)[]>::value, int>::type = 0> |
|||
CONSTEXPR_IF_NOT_DEBUG Span(T* begin, T* end) noexcept : m_data(begin), m_size(end - begin) |
|||
{ |
|||
ASSERT_IF_DEBUG(end >= begin); |
|||
} |
|||
|
|||
/** Implicit conversion of spans between compatible types.
|
|||
* |
|||
* Specifically, if a pointer to an array of type O can be implicitly converted to a pointer to an array of type |
|||
* C, then permit implicit conversion of Span<O> to Span<C>. This matches the behavior of the corresponding |
|||
* C++20 std::span constructor. |
|||
* |
|||
* For example this means that a Span<T> can be converted into a Span<const T>. |
|||
*/ |
|||
template <typename O, typename std::enable_if<std::is_convertible<O (*)[], C (*)[]>::value, int>::type = 0> |
|||
constexpr Span(const Span<O>& other) noexcept : m_data(other.m_data), m_size(other.m_size) {} |
|||
|
|||
/** Default copy constructor. */ |
|||
constexpr Span(const Span&) noexcept = default; |
|||
|
|||
/** Default assignment operator. */ |
|||
Span& operator=(const Span& other) noexcept = default; |
|||
|
|||
/** Construct a Span from an array. This matches the corresponding C++20 std::span constructor. */ |
|||
template <int N> |
|||
constexpr Span(C (&a)[N]) noexcept : m_data(a), m_size(N) {} |
|||
|
|||
/** Construct a Span for objects with .data() and .size() (std::string, std::array, std::vector, ...).
|
|||
* |
|||
* This implements a subset of the functionality provided by the C++20 std::span range-based constructor. |
|||
* |
|||
* To prevent surprises, only Spans for constant value types are supported when passing in temporaries. |
|||
* Note that this restriction does not exist when converting arrays or other Spans (see above). |
|||
*/ |
|||
template <typename V> |
|||
constexpr Span(V& other SPAN_ATTR_LIFETIMEBOUND, |
|||
typename std::enable_if<!is_Span<V>::value && |
|||
std::is_convertible<typename std::remove_pointer<decltype(std::declval<V&>().data())>::type (*)[], C (*)[]>::value && |
|||
std::is_convertible<decltype(std::declval<V&>().size()), std::size_t>::value, std::nullptr_t>::type = nullptr) |
|||
: m_data(other.data()), m_size(other.size()){} |
|||
|
|||
template <typename V> |
|||
constexpr Span(const V& other SPAN_ATTR_LIFETIMEBOUND, |
|||
typename std::enable_if<!is_Span<V>::value && |
|||
std::is_convertible<typename std::remove_pointer<decltype(std::declval<const V&>().data())>::type (*)[], C (*)[]>::value && |
|||
std::is_convertible<decltype(std::declval<const V&>().size()), std::size_t>::value, std::nullptr_t>::type = nullptr) |
|||
: m_data(other.data()), m_size(other.size()){} |
|||
|
|||
constexpr C* data() const noexcept { return m_data; } |
|||
constexpr C* begin() const noexcept { return m_data; } |
|||
constexpr C* end() const noexcept { return m_data + m_size; } |
|||
CONSTEXPR_IF_NOT_DEBUG C& front() const noexcept |
|||
{ |
|||
ASSERT_IF_DEBUG(size() > 0); |
|||
return m_data[0]; |
|||
} |
|||
CONSTEXPR_IF_NOT_DEBUG C& back() const noexcept |
|||
{ |
|||
ASSERT_IF_DEBUG(size() > 0); |
|||
return m_data[m_size - 1]; |
|||
} |
|||
constexpr std::size_t size() const noexcept { return m_size; } |
|||
constexpr bool empty() const noexcept { return size() == 0; } |
|||
CONSTEXPR_IF_NOT_DEBUG C& operator[](std::size_t pos) const noexcept |
|||
{ |
|||
ASSERT_IF_DEBUG(size() > pos); |
|||
return m_data[pos]; |
|||
} |
|||
CONSTEXPR_IF_NOT_DEBUG Span<C> subspan(std::size_t offset) const noexcept |
|||
{ |
|||
ASSERT_IF_DEBUG(size() >= offset); |
|||
return Span<C>(m_data + offset, m_size - offset); |
|||
} |
|||
CONSTEXPR_IF_NOT_DEBUG Span<C> subspan(std::size_t offset, std::size_t count) const noexcept |
|||
{ |
|||
ASSERT_IF_DEBUG(size() >= offset + count); |
|||
return Span<C>(m_data + offset, count); |
|||
} |
|||
CONSTEXPR_IF_NOT_DEBUG Span<C> first(std::size_t count) const noexcept |
|||
{ |
|||
ASSERT_IF_DEBUG(size() >= count); |
|||
return Span<C>(m_data, count); |
|||
} |
|||
CONSTEXPR_IF_NOT_DEBUG Span<C> last(std::size_t count) const noexcept |
|||
{ |
|||
ASSERT_IF_DEBUG(size() >= count); |
|||
return Span<C>(m_data + m_size - count, count); |
|||
} |
|||
|
|||
friend constexpr bool operator==(const Span& a, const Span& b) noexcept { return a.size() == b.size() && std::equal(a.begin(), a.end(), b.begin()); } |
|||
friend constexpr bool operator!=(const Span& a, const Span& b) noexcept { return !(a == b); } |
|||
friend constexpr bool operator<(const Span& a, const Span& b) noexcept { return std::lexicographical_compare(a.begin(), a.end(), b.begin(), b.end()); } |
|||
friend constexpr bool operator<=(const Span& a, const Span& b) noexcept { return !(b < a); } |
|||
friend constexpr bool operator>(const Span& a, const Span& b) noexcept { return (b < a); } |
|||
friend constexpr bool operator>=(const Span& a, const Span& b) noexcept { return !(a < b); } |
|||
|
|||
template <typename O> friend class Span; |
|||
}; |
|||
|
|||
// MakeSpan helps constructing a Span of the right type automatically.
|
|||
/** MakeSpan for arrays: */ |
|||
template <typename A, int N> Span<A> constexpr MakeSpan(A (&a)[N]) { return Span<A>(a, N); } |
|||
/** MakeSpan for temporaries / rvalue references, only supporting const output. */ |
|||
template <typename V> constexpr auto MakeSpan(V&& v SPAN_ATTR_LIFETIMEBOUND) -> typename std::enable_if<!std::is_lvalue_reference<V>::value, Span<const typename std::remove_pointer<decltype(v.data())>::type>>::type { return std::forward<V>(v); } |
|||
/** MakeSpan for (lvalue) references, supporting mutable output. */ |
|||
template <typename V> constexpr auto MakeSpan(V& v SPAN_ATTR_LIFETIMEBOUND) -> Span<typename std::remove_pointer<decltype(v.data())>::type> { return v; } |
|||
|
|||
/** Pop the last element off a span, and return a reference to that element. */ |
|||
template <typename T> |
|||
T& SpanPopBack(Span<T>& span) |
|||
{ |
|||
size_t size = span.size(); |
|||
ASSERT_IF_DEBUG(size > 0); |
|||
T& back = span[size - 1]; |
|||
span = Span<T>(span.data(), size - 1); |
|||
return back; |
|||
} |
|||
|
|||
// Helper functions to safely cast to unsigned char pointers.
|
|||
inline unsigned char* UCharCast(char* c) { return (unsigned char*)c; } |
|||
inline unsigned char* UCharCast(unsigned char* c) { return c; } |
|||
inline const unsigned char* UCharCast(const char* c) { return (unsigned char*)c; } |
|||
inline const unsigned char* UCharCast(const unsigned char* c) { return c; } |
|||
|
|||
// Helper function to safely convert a Span to a Span<[const] unsigned char>.
|
|||
template <typename T> constexpr auto UCharSpanCast(Span<T> s) -> Span<typename std::remove_pointer<decltype(UCharCast(s.data()))>::type> { return {UCharCast(s.data()), s.size()}; } |
|||
|
|||
/** Like MakeSpan, but for (const) unsigned char member types only. Only works for (un)signed char containers. */ |
|||
template <typename V> constexpr auto MakeUCharSpan(V&& v) -> decltype(UCharSpanCast(MakeSpan(std::forward<V>(v)))) { return UCharSpanCast(MakeSpan(std::forward<V>(v))); } |
|||
|
|||
#endif |
@ -0,0 +1,47 @@ |
|||
// Copyright (c) 2015-2020 The Bitcoin Core developers
|
|||
// Copyright (c) 2017 The Zcash developers
|
|||
// Copyright (c) 2016-2022 The Hush developers
|
|||
// Distributed under the GPLv3 software license, see the accompanying
|
|||
// file COPYING or https://www.gnu.org/licenses/gpl-3.0.en.html
|
|||
|
|||
#include <fs.h> |
|||
#include <limits> |
|||
#include <stdio.h> |
|||
#include <string> |
|||
#include <utility> |
|||
|
|||
std::pair<bool,std::string> ReadBinaryFile(const fs::path &filename, size_t maxsize=std::numeric_limits<size_t>::max()) |
|||
{ |
|||
FILE *f = fsbridge::fopen(filename, "rb"); |
|||
if (f == nullptr) |
|||
return std::make_pair(false,""); |
|||
std::string retval; |
|||
char buffer[128]; |
|||
do { |
|||
const size_t n = fread(buffer, 1, sizeof(buffer), f); |
|||
// Check for reading errors so we don't return any data if we couldn't
|
|||
// read the entire file (or up to maxsize)
|
|||
if (ferror(f)) { |
|||
fclose(f); |
|||
return std::make_pair(false,""); |
|||
} |
|||
retval.append(buffer, buffer+n); |
|||
} while (!feof(f) && retval.size() <= maxsize); |
|||
fclose(f); |
|||
return std::make_pair(true,retval); |
|||
} |
|||
|
|||
bool WriteBinaryFile(const fs::path &filename, const std::string &data) |
|||
{ |
|||
FILE *f = fsbridge::fopen(filename, "wb"); |
|||
if (f == nullptr) |
|||
return false; |
|||
if (fwrite(data.data(), 1, data.size(), f) != data.size()) { |
|||
fclose(f); |
|||
return false; |
|||
} |
|||
if (fclose(f) != 0) { |
|||
return false; |
|||
} |
|||
return true; |
|||
} |
@ -0,0 +1,29 @@ |
|||
// Copyright (c) 2015-2020 The Bitcoin Core developers
|
|||
// Copyright (c) 2016-2022 The Hush developers
|
|||
// Distributed under the GPLv3 software license, see the accompanying
|
|||
// file COPYING or https://www.gnu.org/licenses/gpl-3.0.en.html
|
|||
|
|||
#ifndef BITCOIN_UTIL_READWRITEFILE_H |
|||
#define BITCOIN_UTIL_READWRITEFILE_H |
|||
|
|||
#include <fs.h> |
|||
|
|||
#include <limits> |
|||
#include <string> |
|||
#include <utility> |
|||
|
|||
/** Read full contents of a file and return them in a std::string.
|
|||
* Returns a pair <status, string>. |
|||
* If an error occurred, status will be false, otherwise status will be true and the data will be returned in string. |
|||
* |
|||
* @param maxsize Puts a maximum size limit on the file that is read. If the file is larger than this, truncated data |
|||
* (with len > maxsize) will be returned. |
|||
*/ |
|||
std::pair<bool,std::string> ReadBinaryFile(const fs::path &filename, size_t maxsize=std::numeric_limits<size_t>::max()); |
|||
|
|||
/** Write contents of std::string to a file.
|
|||
* @return true on success. |
|||
*/ |
|||
bool WriteBinaryFile(const fs::path &filename, const std::string &data); |
|||
|
|||
#endif /* BITCOIN_UTIL_READWRITEFILE_H */ |
@ -0,0 +1,328 @@ |
|||
// Copyright (c) 2020-2021 The Bitcoin Core developers
|
|||
// Copyright (c) 2016-2022 The Hush developers
|
|||
// Distributed under the GPLv3 software license, see the accompanying
|
|||
// file COPYING or https://www.gnu.org/licenses/gpl-3.0.en.html
|
|||
|
|||
#include <compat.h> |
|||
#include <tinyformat.h> |
|||
#include <util/sock.h> |
|||
#include <utiltime.h> |
|||
#include <util.h> |
|||
#include <codecvt> |
|||
#include <cwchar> |
|||
#include <locale> |
|||
#include <stdexcept> |
|||
#include <string> |
|||
|
|||
#ifdef USE_POLL |
|||
#include <poll.h> |
|||
#endif |
|||
|
|||
static inline bool IOErrorIsPermanent(int err) |
|||
{ |
|||
return err != WSAEAGAIN && err != WSAEINTR && err != WSAEWOULDBLOCK && err != WSAEINPROGRESS; |
|||
} |
|||
|
|||
Sock::Sock() : m_socket(INVALID_SOCKET) {} |
|||
|
|||
Sock::Sock(SOCKET s) : m_socket(s) {} |
|||
|
|||
Sock::Sock(Sock&& other) |
|||
{ |
|||
m_socket = other.m_socket; |
|||
other.m_socket = INVALID_SOCKET; |
|||
} |
|||
|
|||
Sock::~Sock() { Reset(); } |
|||
|
|||
Sock& Sock::operator=(Sock&& other) |
|||
{ |
|||
Reset(); |
|||
m_socket = other.m_socket; |
|||
other.m_socket = INVALID_SOCKET; |
|||
return *this; |
|||
} |
|||
|
|||
SOCKET Sock::Get() const { return m_socket; } |
|||
|
|||
SOCKET Sock::Release() |
|||
{ |
|||
const SOCKET s = m_socket; |
|||
m_socket = INVALID_SOCKET; |
|||
return s; |
|||
} |
|||
|
|||
void Sock::Reset() { CloseSocket(m_socket); } |
|||
|
|||
ssize_t Sock::Send(const void* data, size_t len, int flags) const |
|||
{ |
|||
return send(m_socket, static_cast<const char*>(data), len, flags); |
|||
} |
|||
|
|||
ssize_t Sock::Recv(void* buf, size_t len, int flags) const |
|||
{ |
|||
return recv(m_socket, static_cast<char*>(buf), len, flags); |
|||
} |
|||
|
|||
int Sock::Connect(const sockaddr* addr, socklen_t addr_len) const |
|||
{ |
|||
return connect(m_socket, addr, addr_len); |
|||
} |
|||
|
|||
int Sock::GetSockOpt(int level, int opt_name, void* opt_val, socklen_t* opt_len) const |
|||
{ |
|||
return getsockopt(m_socket, level, opt_name, static_cast<char*>(opt_val), opt_len); |
|||
} |
|||
|
|||
bool Sock::Wait(std::chrono::milliseconds timeout, Event requested, Event* occurred) const |
|||
{ |
|||
#ifdef USE_POLL |
|||
pollfd fd; |
|||
fd.fd = m_socket; |
|||
fd.events = 0; |
|||
if (requested & RECV) { |
|||
fd.events |= POLLIN; |
|||
} |
|||
if (requested & SEND) { |
|||
fd.events |= POLLOUT; |
|||
} |
|||
|
|||
if (poll(&fd, 1, count_milliseconds(timeout)) == SOCKET_ERROR) { |
|||
return false; |
|||
} |
|||
|
|||
if (occurred != nullptr) { |
|||
*occurred = 0; |
|||
if (fd.revents & POLLIN) { |
|||
*occurred |= RECV; |
|||
} |
|||
if (fd.revents & POLLOUT) { |
|||
*occurred |= SEND; |
|||
} |
|||
} |
|||
|
|||
return true; |
|||
#else |
|||
if (!IsSelectableSocket(m_socket)) { |
|||
return false; |
|||
} |
|||
|
|||
fd_set fdset_recv; |
|||
fd_set fdset_send; |
|||
FD_ZERO(&fdset_recv); |
|||
FD_ZERO(&fdset_send); |
|||
|
|||
if (requested & RECV) { |
|||
FD_SET(m_socket, &fdset_recv); |
|||
} |
|||
|
|||
if (requested & SEND) { |
|||
FD_SET(m_socket, &fdset_send); |
|||
} |
|||
|
|||
timeval timeout_struct = MillisToTimeval(timeout.count()); |
|||
|
|||
if (select(m_socket + 1, &fdset_recv, &fdset_send, nullptr, &timeout_struct) == SOCKET_ERROR) { |
|||
return false; |
|||
} |
|||
|
|||
if (occurred != nullptr) { |
|||
*occurred = 0; |
|||
if (FD_ISSET(m_socket, &fdset_recv)) { |
|||
*occurred |= RECV; |
|||
} |
|||
if (FD_ISSET(m_socket, &fdset_send)) { |
|||
*occurred |= SEND; |
|||
} |
|||
} |
|||
|
|||
return true; |
|||
#endif /* USE_POLL */ |
|||
} |
|||
|
|||
void Sock::SendComplete(const std::string& data, |
|||
std::chrono::milliseconds timeout) const |
|||
{ |
|||
const auto deadline = GetTime<std::chrono::milliseconds>() + timeout; |
|||
size_t sent{0}; |
|||
|
|||
for (;;) { |
|||
const ssize_t ret{Send(data.data() + sent, data.size() - sent, MSG_NOSIGNAL)}; |
|||
|
|||
if (ret > 0) { |
|||
sent += static_cast<size_t>(ret); |
|||
if (sent == data.size()) { |
|||
break; |
|||
} |
|||
} else { |
|||
const int err{WSAGetLastError()}; |
|||
if (IOErrorIsPermanent(err)) { |
|||
throw std::runtime_error(strprintf("send(): %s", NetworkErrorString(err))); |
|||
} |
|||
} |
|||
|
|||
const auto now = GetTime<std::chrono::milliseconds>(); |
|||
|
|||
if (now >= deadline) { |
|||
throw std::runtime_error(strprintf( |
|||
"Send timeout (sent only %u of %u bytes before that)", sent, data.size())); |
|||
} |
|||
|
|||
// Wait for a short while (or the socket to become ready for sending) before retrying
|
|||
// if nothing was sent.
|
|||
const auto wait_time = std::min(deadline - now, std::chrono::milliseconds{MAX_WAIT_FOR_IO}); |
|||
(void)Wait(wait_time, SEND); |
|||
} |
|||
} |
|||
|
|||
std::string Sock::RecvUntilTerminator(uint8_t terminator, |
|||
std::chrono::milliseconds timeout, |
|||
size_t max_data) const |
|||
{ |
|||
const auto deadline = GetTime<std::chrono::milliseconds>() + timeout; |
|||
std::string data; |
|||
bool terminator_found{false}; |
|||
|
|||
// We must not consume any bytes past the terminator from the socket.
|
|||
// One option is to read one byte at a time and check if we have read a terminator.
|
|||
// However that is very slow. Instead, we peek at what is in the socket and only read
|
|||
// as many bytes as possible without crossing the terminator.
|
|||
// Reading 64 MiB of random data with 262526 terminator chars takes 37 seconds to read
|
|||
// one byte at a time VS 0.71 seconds with the "peek" solution below. Reading one byte
|
|||
// at a time is about 50 times slower.
|
|||
|
|||
for (;;) { |
|||
if (data.size() >= max_data) { |
|||
throw std::runtime_error( |
|||
strprintf("Received too many bytes without a terminator (%u)", data.size())); |
|||
} |
|||
|
|||
char buf[512]; |
|||
|
|||
const ssize_t peek_ret{Recv(buf, std::min(sizeof(buf), max_data - data.size()), MSG_PEEK)}; |
|||
|
|||
switch (peek_ret) { |
|||
case -1: { |
|||
const int err{WSAGetLastError()}; |
|||
if (IOErrorIsPermanent(err)) { |
|||
throw std::runtime_error(strprintf("recv(): %s", NetworkErrorString(err))); |
|||
} |
|||
break; |
|||
} |
|||
case 0: |
|||
throw std::runtime_error("Connection unexpectedly closed by peer"); |
|||
default: |
|||
auto end = buf + peek_ret; |
|||
auto terminator_pos = std::find(buf, end, terminator); |
|||
terminator_found = terminator_pos != end; |
|||
|
|||
const size_t try_len{terminator_found ? terminator_pos - buf + 1 : |
|||
static_cast<size_t>(peek_ret)}; |
|||
|
|||
const ssize_t read_ret{Recv(buf, try_len, 0)}; |
|||
|
|||
if (read_ret < 0 || static_cast<size_t>(read_ret) != try_len) { |
|||
throw std::runtime_error( |
|||
strprintf("recv() returned %u bytes on attempt to read %u bytes but previous " |
|||
"peek claimed %u bytes are available", |
|||
read_ret, try_len, peek_ret)); |
|||
} |
|||
|
|||
// Don't include the terminator in the output.
|
|||
const size_t append_len{terminator_found ? try_len - 1 : try_len}; |
|||
|
|||
data.append(buf, buf + append_len); |
|||
|
|||
if (terminator_found) { |
|||
return data; |
|||
} |
|||
} |
|||
|
|||
const auto now = GetTime<std::chrono::milliseconds>(); |
|||
|
|||
if (now >= deadline) { |
|||
throw std::runtime_error(strprintf( |
|||
"Receive timeout (received %u bytes without terminator before that)", data.size())); |
|||
} |
|||
|
|||
// Wait for a short while (or the socket to become ready for reading) before retrying.
|
|||
const auto wait_time = std::min(deadline - now, std::chrono::milliseconds{MAX_WAIT_FOR_IO}); |
|||
(void)Wait(wait_time, RECV); |
|||
} |
|||
} |
|||
|
|||
bool Sock::IsConnected(std::string& errmsg) const |
|||
{ |
|||
if (m_socket == INVALID_SOCKET) { |
|||
errmsg = "not connected"; |
|||
return false; |
|||
} |
|||
|
|||
char c; |
|||
switch (Recv(&c, sizeof(c), MSG_PEEK)) { |
|||
case -1: { |
|||
const int err = WSAGetLastError(); |
|||
if (IOErrorIsPermanent(err)) { |
|||
errmsg = NetworkErrorString(err); |
|||
return false; |
|||
} |
|||
return true; |
|||
} |
|||
case 0: |
|||
errmsg = "closed"; |
|||
return false; |
|||
default: |
|||
return true; |
|||
} |
|||
} |
|||
|
|||
#ifdef WIN32 |
|||
std::string NetworkErrorString(int err) |
|||
{ |
|||
wchar_t buf[256]; |
|||
buf[0] = 0; |
|||
if(FormatMessageW(FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS | FORMAT_MESSAGE_MAX_WIDTH_MASK, |
|||
nullptr, err, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), |
|||
buf, ARRAYSIZE(buf), nullptr)) |
|||
{ |
|||
return strprintf("%s (%d)", std::wstring_convert<std::codecvt_utf8_utf16<wchar_t>,wchar_t>().to_bytes(buf), err); |
|||
} |
|||
else |
|||
{ |
|||
return strprintf("Unknown error (%d)", err); |
|||
} |
|||
} |
|||
#else |
|||
std::string NetworkErrorString(int err) |
|||
{ |
|||
char buf[256]; |
|||
buf[0] = 0; |
|||
/* Too bad there are two incompatible implementations of the
|
|||
* thread-safe strerror. */ |
|||
const char *s; |
|||
#ifdef STRERROR_R_CHAR_P /* GNU variant can return a pointer outside the passed buffer */ |
|||
s = strerror_r(err, buf, sizeof(buf)); |
|||
#else /* POSIX variant always returns message in buffer */ |
|||
s = buf; |
|||
if (strerror_r(err, buf, sizeof(buf))) |
|||
buf[0] = 0; |
|||
#endif |
|||
return strprintf("%s (%d)", s, err); |
|||
} |
|||
#endif |
|||
|
|||
bool CloseSocket(SOCKET& hSocket) |
|||
{ |
|||
if (hSocket == INVALID_SOCKET) |
|||
return false; |
|||
#ifdef WIN32 |
|||
int ret = closesocket(hSocket); |
|||
#else |
|||
int ret = close(hSocket); |
|||
#endif |
|||
if (ret) { |
|||
LogPrintf("Socket close failed: %d. Error: %s\n", hSocket, NetworkErrorString(WSAGetLastError())); |
|||
} |
|||
hSocket = INVALID_SOCKET; |
|||
return ret != SOCKET_ERROR; |
|||
} |
@ -0,0 +1,184 @@ |
|||
// Copyright (c) 2020-2021 The Bitcoin Core developers
|
|||
// Copyright (c) 2016-2022 The Hush developers
|
|||
// Distributed under the GPLv3 software license, see the accompanying
|
|||
// file COPYING or https://www.gnu.org/licenses/gpl-3.0.en.html
|
|||
|
|||
#ifndef BITCOIN_UTIL_SOCK_H |
|||
#define BITCOIN_UTIL_SOCK_H |
|||
|
|||
#include <compat.h> |
|||
// #include <threadinterrupt.h>
|
|||
#include <utiltime.h> |
|||
|
|||
#include <chrono> |
|||
#include <string> |
|||
|
|||
/**
|
|||
* Maximum time to wait for I/O readiness. |
|||
* It will take up until this time to break off in case of an interruption. |
|||
*/ |
|||
static constexpr auto MAX_WAIT_FOR_IO = 1000; |
|||
|
|||
/**
|
|||
* RAII helper class that manages a socket. Mimics `std::unique_ptr`, but instead of a pointer it |
|||
* contains a socket and closes it automatically when it goes out of scope. |
|||
*/ |
|||
class Sock |
|||
{ |
|||
public: |
|||
/**
|
|||
* Default constructor, creates an empty object that does nothing when destroyed. |
|||
*/ |
|||
Sock(); |
|||
|
|||
/**
|
|||
* Take ownership of an existent socket. |
|||
*/ |
|||
explicit Sock(SOCKET s); |
|||
|
|||
/**
|
|||
* Copy constructor, disabled because closing the same socket twice is undesirable. |
|||
*/ |
|||
Sock(const Sock&) = delete; |
|||
|
|||
/**
|
|||
* Move constructor, grab the socket from another object and close ours (if set). |
|||
*/ |
|||
Sock(Sock&& other); |
|||
|
|||
/**
|
|||
* Destructor, close the socket or do nothing if empty. |
|||
*/ |
|||
virtual ~Sock(); |
|||
|
|||
/**
|
|||
* Copy assignment operator, disabled because closing the same socket twice is undesirable. |
|||
*/ |
|||
Sock& operator=(const Sock&) = delete; |
|||
|
|||
/**
|
|||
* Move assignment operator, grab the socket from another object and close ours (if set). |
|||
*/ |
|||
virtual Sock& operator=(Sock&& other); |
|||
|
|||
/**
|
|||
* Get the value of the contained socket. |
|||
* @return socket or INVALID_SOCKET if empty |
|||
*/ |
|||
virtual SOCKET Get() const; |
|||
|
|||
/**
|
|||
* Get the value of the contained socket and drop ownership. It will not be closed by the |
|||
* destructor after this call. |
|||
* @return socket or INVALID_SOCKET if empty |
|||
*/ |
|||
virtual SOCKET Release(); |
|||
|
|||
/**
|
|||
* Close if non-empty. |
|||
*/ |
|||
virtual void Reset(); |
|||
|
|||
/**
|
|||
* send(2) wrapper. Equivalent to `send(this->Get(), data, len, flags);`. Code that uses this |
|||
* wrapper can be unit tested if this method is overridden by a mock Sock implementation. |
|||
*/ |
|||
virtual ssize_t Send(const void* data, size_t len, int flags) const; |
|||
|
|||
/**
|
|||
* recv(2) wrapper. Equivalent to `recv(this->Get(), buf, len, flags);`. Code that uses this |
|||
* wrapper can be unit tested if this method is overridden by a mock Sock implementation. |
|||
*/ |
|||
virtual ssize_t Recv(void* buf, size_t len, int flags) const; |
|||
|
|||
/**
|
|||
* connect(2) wrapper. Equivalent to `connect(this->Get(), addr, addrlen)`. Code that uses this |
|||
* wrapper can be unit tested if this method is overridden by a mock Sock implementation. |
|||
*/ |
|||
virtual int Connect(const sockaddr* addr, socklen_t addr_len) const; |
|||
|
|||
/**
|
|||
* getsockopt(2) wrapper. Equivalent to |
|||
* `getsockopt(this->Get(), level, opt_name, opt_val, opt_len)`. Code that uses this |
|||
* wrapper can be unit tested if this method is overridden by a mock Sock implementation. |
|||
*/ |
|||
virtual int GetSockOpt(int level, |
|||
int opt_name, |
|||
void* opt_val, |
|||
socklen_t* opt_len) const; |
|||
|
|||
using Event = uint8_t; |
|||
|
|||
/**
|
|||
* If passed to `Wait()`, then it will wait for readiness to read from the socket. |
|||
*/ |
|||
static constexpr Event RECV = 0b01; |
|||
|
|||
/**
|
|||
* If passed to `Wait()`, then it will wait for readiness to send to the socket. |
|||
*/ |
|||
static constexpr Event SEND = 0b10; |
|||
|
|||
/**
|
|||
* Wait for readiness for input (recv) or output (send). |
|||
* @param[in] timeout Wait this much for at least one of the requested events to occur. |
|||
* @param[in] requested Wait for those events, bitwise-or of `RECV` and `SEND`. |
|||
* @param[out] occurred If not nullptr and `true` is returned, then upon return this |
|||
* indicates which of the requested events occurred. A timeout is indicated by return |
|||
* value of `true` and `occurred` being set to 0. |
|||
* @return true on success and false otherwise |
|||
*/ |
|||
virtual bool Wait(std::chrono::milliseconds timeout, |
|||
Event requested, |
|||
Event* occurred = nullptr) const; |
|||
|
|||
/* Higher level, convenience, methods. These may throw. */ |
|||
|
|||
/**
|
|||
* Send the given data, retrying on transient errors. |
|||
* @param[in] data Data to send. |
|||
* @param[in] timeout Timeout for the entire operation. |
|||
* @param[in] interrupt If this is signaled then the operation is canceled. |
|||
* @throws std::runtime_error if the operation cannot be completed. In this case only some of |
|||
* the data will be written to the socket. |
|||
*/ |
|||
virtual void SendComplete(const std::string& data, |
|||
std::chrono::milliseconds timeout) const; |
|||
|
|||
/**
|
|||
* Read from socket until a terminator character is encountered. Will never consume bytes past |
|||
* the terminator from the socket. |
|||
* @param[in] terminator Character up to which to read from the socket. |
|||
* @param[in] timeout Timeout for the entire operation. |
|||
* @param[in] interrupt If this is signaled then the operation is canceled. |
|||
* @param[in] max_data The maximum amount of data (in bytes) to receive. If this many bytes |
|||
* are received and there is still no terminator, then this method will throw an exception. |
|||
* @return The data that has been read, without the terminating character. |
|||
* @throws std::runtime_error if the operation cannot be completed. In this case some bytes may |
|||
* have been consumed from the socket. |
|||
*/ |
|||
virtual std::string RecvUntilTerminator(uint8_t terminator, |
|||
std::chrono::milliseconds timeout, |
|||
size_t max_data) const; |
|||
|
|||
/**
|
|||
* Check if still connected. |
|||
* @param[out] errmsg The error string, if the socket has been disconnected. |
|||
* @return true if connected |
|||
*/ |
|||
virtual bool IsConnected(std::string& errmsg) const; |
|||
|
|||
protected: |
|||
/**
|
|||
* Contained socket. `INVALID_SOCKET` designates the object is empty. |
|||
*/ |
|||
SOCKET m_socket; |
|||
}; |
|||
|
|||
/** Return readable error string for a network error code */ |
|||
std::string NetworkErrorString(int err); |
|||
|
|||
/** Close socket and set hSocket to INVALID_SOCKET */ |
|||
bool CloseSocket(SOCKET& hSocket); |
|||
|
|||
#endif // BITCOIN_UTIL_SOCK_H
|
@ -0,0 +1,68 @@ |
|||
// Copyright (c) 2018-2019 The Bitcoin Core developers
|
|||
// Copyright (c) 2016-2022 The Hush developers
|
|||
// Distributed under the GPLv3 software license, see the accompanying
|
|||
// file COPYING or https://www.gnu.org/licenses/gpl-3.0.en.html
|
|||
|
|||
#include <util/spanparsing.h> |
|||
|
|||
#include <span.h> |
|||
|
|||
#include <string> |
|||
#include <vector> |
|||
|
|||
namespace spanparsing { |
|||
|
|||
bool Const(const std::string& str, Span<const char>& sp) |
|||
{ |
|||
if ((size_t)sp.size() >= str.size() && std::equal(str.begin(), str.end(), sp.begin())) { |
|||
sp = sp.subspan(str.size()); |
|||
return true; |
|||
} |
|||
return false; |
|||
} |
|||
|
|||
bool Func(const std::string& str, Span<const char>& sp) |
|||
{ |
|||
if ((size_t)sp.size() >= str.size() + 2 && sp[str.size()] == '(' && sp[sp.size() - 1] == ')' && std::equal(str.begin(), str.end(), sp.begin())) { |
|||
sp = sp.subspan(str.size() + 1, sp.size() - str.size() - 2); |
|||
return true; |
|||
} |
|||
return false; |
|||
} |
|||
|
|||
Span<const char> Expr(Span<const char>& sp) |
|||
{ |
|||
int level = 0; |
|||
auto it = sp.begin(); |
|||
while (it != sp.end()) { |
|||
if (*it == '(' || *it == '{') { |
|||
++level; |
|||
} else if (level && (*it == ')' || *it == '}')) { |
|||
--level; |
|||
} else if (level == 0 && (*it == ')' || *it == '}' || *it == ',')) { |
|||
break; |
|||
} |
|||
++it; |
|||
} |
|||
Span<const char> ret = sp.first(it - sp.begin()); |
|||
sp = sp.subspan(it - sp.begin()); |
|||
return ret; |
|||
} |
|||
|
|||
std::vector<Span<const char>> Split(const Span<const char>& sp, char sep) |
|||
{ |
|||
std::vector<Span<const char>> ret; |
|||
auto it = sp.begin(); |
|||
auto start = it; |
|||
while (it != sp.end()) { |
|||
if (*it == sep) { |
|||
ret.emplace_back(start, it); |
|||
start = it + 1; |
|||
} |
|||
++it; |
|||
} |
|||
ret.emplace_back(start, it); |
|||
return ret; |
|||
} |
|||
|
|||
} // namespace spanparsing
|
@ -0,0 +1,50 @@ |
|||
// Copyright (c) 2018-2019 The Bitcoin Core developers
|
|||
// Copyright (c) 2016-2022 The Hush developers
|
|||
// Distributed under the GPLv3 software license, see the accompanying
|
|||
// file COPYING or https://www.gnu.org/licenses/gpl-3.0.en.html
|
|||
|
|||
#ifndef BITCOIN_UTIL_SPANPARSING_H |
|||
#define BITCOIN_UTIL_SPANPARSING_H |
|||
|
|||
#include <span.h> |
|||
#include <string> |
|||
#include <vector> |
|||
|
|||
namespace spanparsing { |
|||
|
|||
/** Parse a constant.
|
|||
* |
|||
* If sp's initial part matches str, sp is updated to skip that part, and true is returned. |
|||
* Otherwise sp is unmodified and false is returned. |
|||
*/ |
|||
bool Const(const std::string& str, Span<const char>& sp); |
|||
|
|||
/** Parse a function call.
|
|||
* |
|||
* If sp's initial part matches str + "(", and sp ends with ")", sp is updated to be the |
|||
* section between the braces, and true is returned. Otherwise sp is unmodified and false |
|||
* is returned. |
|||
*/ |
|||
bool Func(const std::string& str, Span<const char>& sp); |
|||
|
|||
/** Extract the expression that sp begins with.
|
|||
* |
|||
* This function will return the initial part of sp, up to (but not including) the first |
|||
* comma or closing brace, skipping ones that are surrounded by braces. So for example, |
|||
* for "foo(bar(1),2),3" the initial part "foo(bar(1),2)" will be returned. sp will be |
|||
* updated to skip the initial part that is returned. |
|||
*/ |
|||
Span<const char> Expr(Span<const char>& sp); |
|||
|
|||
/** Split a string on every instance of sep, returning a vector.
|
|||
* |
|||
* If sep does not occur in sp, a singleton with the entirety of sp is returned. |
|||
* |
|||
* Note that this function does not care about braces, so splitting |
|||
* "foo(bar(1),2),3) on ',' will return {"foo(bar(1)", "2)", "3)"}. |
|||
*/ |
|||
std::vector<Span<const char>> Split(const Span<const char>& sp, char sep); |
|||
|
|||
} // namespace spanparsing
|
|||
|
|||
#endif // BITCOIN_UTIL_SPANPARSING_H
|
@ -0,0 +1,655 @@ |
|||
// Copyright (c) 2009-2010 Satoshi Nakamoto
|
|||
// Copyright (c) 2009-2020 The Bitcoin Core developers
|
|||
// Copyright (c) 2016-2022 The Hush developers
|
|||
// Distributed under the GPLv3 software license, see the accompanying
|
|||
// file COPYING or https://www.gnu.org/licenses/gpl-3.0.en.html
|
|||
|
|||
#include "util/strencodings.h" |
|||
#include "util/string.h" |
|||
|
|||
#include <tinyformat.h> |
|||
|
|||
#include <algorithm> |
|||
#include <cstdlib> |
|||
#include <cstring> |
|||
#include <iomanip> |
|||
#include <errno.h> |
|||
#include <limits> |
|||
|
|||
using namespace std; |
|||
|
|||
static const std::string CHARS_ALPHA_NUM = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789"; |
|||
|
|||
static const std::string SAFE_CHARS[] = |
|||
{ |
|||
CHARS_ALPHA_NUM + " .,;-_/:?@()", // SAFE_CHARS_DEFAULT
|
|||
CHARS_ALPHA_NUM + " .,;-_?@", // SAFE_CHARS_UA_COMMENT
|
|||
CHARS_ALPHA_NUM + ".-_", // SAFE_CHARS_FILENAME
|
|||
CHARS_ALPHA_NUM + "!*'();:@&=+$,/?#[]-_.~%", // SAFE_CHARS_URI
|
|||
}; |
|||
|
|||
std::string SanitizeString(const std::string& str, int rule) |
|||
{ |
|||
std::string strResult; |
|||
for (std::string::size_type i = 0; i < str.size(); i++) |
|||
{ |
|||
if (SAFE_CHARS[rule].find(str[i]) != std::string::npos) |
|||
strResult.push_back(str[i]); |
|||
} |
|||
return strResult; |
|||
} |
|||
|
|||
string SanitizeFilename(const string& str) |
|||
{ |
|||
/**
|
|||
* safeChars chosen to restrict filename, keeping it simple to avoid cross-platform issues. |
|||
* http://stackoverflow.com/a/2306003
|
|||
*/ |
|||
static string safeChars("abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ01234567890"); |
|||
string strResult; |
|||
for (std::string::size_type i = 0; i < str.size(); i++) |
|||
{ |
|||
if (safeChars.find(str[i]) != std::string::npos) |
|||
strResult.push_back(str[i]); |
|||
} |
|||
return strResult; |
|||
} |
|||
|
|||
std::string HexInt(uint32_t val) |
|||
{ |
|||
std::stringstream ss; |
|||
ss << std::setfill('0') << std::setw(sizeof(uint32_t) * 2) << std::hex << val; |
|||
return ss.str(); |
|||
} |
|||
|
|||
uint32_t ParseHexToUInt32(const std::string& str) { |
|||
std::istringstream converter(str); |
|||
uint32_t value; |
|||
converter >> std::hex >> value; |
|||
return value; |
|||
} |
|||
|
|||
const signed char p_util_hexdigit[256] = |
|||
{ -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1, |
|||
-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1, |
|||
-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1, |
|||
0,1,2,3,4,5,6,7,8,9,-1,-1,-1,-1,-1,-1, |
|||
-1,0xa,0xb,0xc,0xd,0xe,0xf,-1,-1,-1,-1,-1,-1,-1,-1,-1, |
|||
-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1, |
|||
-1,0xa,0xb,0xc,0xd,0xe,0xf,-1,-1,-1,-1,-1,-1,-1,-1,-1, |
|||
-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1, |
|||
-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1, |
|||
-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1, |
|||
-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1, |
|||
-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1, |
|||
-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1, |
|||
-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1, |
|||
-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1, |
|||
-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1, }; |
|||
|
|||
signed char HexDigit(char c) |
|||
{ |
|||
return p_util_hexdigit[(unsigned char)c]; |
|||
} |
|||
|
|||
bool IsHex(const std::string& str) |
|||
{ |
|||
for(std::string::const_iterator it(str.begin()); it != str.end(); ++it) |
|||
{ |
|||
if (HexDigit(*it) < 0) |
|||
return false; |
|||
} |
|||
return (str.size() > 0) && (str.size()%2 == 0); |
|||
} |
|||
|
|||
bool IsHexNumber(const std::string& str) |
|||
{ |
|||
size_t starting_location = 0; |
|||
if (str.size() > 2 && *str.begin() == '0' && *(str.begin()+1) == 'x') { |
|||
starting_location = 2; |
|||
} |
|||
for (const char c : str.substr(starting_location)) { |
|||
if (HexDigit(c) < 0) return false; |
|||
} |
|||
// Return false for empty string or "0x".
|
|||
return (str.size() > starting_location); |
|||
} |
|||
|
|||
std::vector<unsigned char> ParseHex(const char* psz) |
|||
{ |
|||
// convert hex dump to vector
|
|||
std::vector<unsigned char> vch; |
|||
while (true) |
|||
{ |
|||
while (IsSpace(*psz)) |
|||
psz++; |
|||
signed char c = HexDigit(*psz++); |
|||
if (c == (signed char)-1) |
|||
break; |
|||
unsigned char n = (c << 4); |
|||
c = HexDigit(*psz++); |
|||
if (c == (signed char)-1) |
|||
break; |
|||
n |= c; |
|||
vch.push_back(n); |
|||
} |
|||
return vch; |
|||
} |
|||
|
|||
std::vector<unsigned char> ParseHex(const std::string& str) |
|||
{ |
|||
return ParseHex(str.c_str()); |
|||
} |
|||
|
|||
void SplitHostPort(std::string in, int &portOut, std::string &hostOut) { |
|||
size_t colon = in.find_last_of(':'); |
|||
// if a : is found, and it either follows a [...], or no other : is in the string, treat it as port separator
|
|||
bool fHaveColon = colon != in.npos; |
|||
bool fBracketed = fHaveColon && (in[0]=='[' && in[colon-1]==']'); // if there is a colon, and in[0]=='[', colon is not 0, so in[colon-1] is safe
|
|||
bool fMultiColon = fHaveColon && (in.find_last_of(':',colon-1) != in.npos); |
|||
if (fHaveColon && (colon==0 || fBracketed || !fMultiColon)) { |
|||
int32_t n; |
|||
if (ParseInt32(in.substr(colon + 1), &n) && n > 0 && n < 0x10000) { |
|||
in = in.substr(0, colon); |
|||
portOut = n; |
|||
} |
|||
} |
|||
if (in.size()>0 && in[0] == '[' && in[in.size()-1] == ']') |
|||
hostOut = in.substr(1, in.size()-2); |
|||
else |
|||
hostOut = in; |
|||
} |
|||
|
|||
std::string EncodeBase64(Span<const unsigned char> input) |
|||
{ |
|||
static const char *pbase64 = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"; |
|||
|
|||
std::string str; |
|||
str.reserve(((input.size() + 2) / 3) * 4); |
|||
ConvertBits<8, 6, true>([&](int v) { str += pbase64[v]; }, input.begin(), input.end()); |
|||
while (str.size() % 4) str += '='; |
|||
return str; |
|||
} |
|||
|
|||
std::string EncodeBase64(const unsigned char* pch, size_t len) |
|||
{ |
|||
static const char *pbase64 = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"; |
|||
|
|||
std::string str; |
|||
str.reserve(((len + 2) / 3) * 4); |
|||
ConvertBits<8, 6, true>([&](int v) { str += pbase64[v]; }, pch, pch + len); |
|||
while (str.size() % 4) str += '='; |
|||
return str; |
|||
} |
|||
|
|||
std::string EncodeBase64(const std::string& str) |
|||
{ |
|||
return EncodeBase64((const unsigned char*)str.data(), str.size()); |
|||
} |
|||
|
|||
std::vector<unsigned char> DecodeBase64(const char* p, bool* pf_invalid) |
|||
{ |
|||
static const int decode64_table[256] = |
|||
{ |
|||
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, |
|||
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, |
|||
-1, -1, -1, 62, -1, -1, -1, 63, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, -1, -1, |
|||
-1, -1, -1, -1, -1, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, |
|||
15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, -1, -1, -1, -1, -1, -1, 26, 27, 28, |
|||
29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, |
|||
49, 50, 51, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, |
|||
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, |
|||
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, |
|||
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, |
|||
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, |
|||
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, |
|||
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 |
|||
}; |
|||
|
|||
const char* e = p; |
|||
std::vector<uint8_t> val; |
|||
val.reserve(strlen(p)); |
|||
while (*p != 0) { |
|||
int x = decode64_table[(unsigned char)*p]; |
|||
if (x == -1) break; |
|||
val.push_back(x); |
|||
++p; |
|||
} |
|||
|
|||
std::vector<unsigned char> ret; |
|||
ret.reserve((val.size() * 3) / 4); |
|||
bool valid = ConvertBits<6, 8, false>([&](unsigned char c) { ret.push_back(c); }, val.begin(), val.end()); |
|||
|
|||
const char* q = p; |
|||
while (valid && *p != 0) { |
|||
if (*p != '=') { |
|||
valid = false; |
|||
break; |
|||
} |
|||
++p; |
|||
} |
|||
valid = valid && (p - e) % 4 == 0 && p - q < 4; |
|||
if (pf_invalid) *pf_invalid = !valid; |
|||
|
|||
return ret; |
|||
} |
|||
|
|||
std::string DecodeBase64(const std::string& str, bool* pf_invalid) |
|||
{ |
|||
if (!ValidAsCString(str)) { |
|||
if (pf_invalid) { |
|||
*pf_invalid = true; |
|||
} |
|||
return {}; |
|||
} |
|||
std::vector<unsigned char> vchRet = DecodeBase64(str.c_str(), pf_invalid); |
|||
return std::string((const char*)vchRet.data(), vchRet.size()); |
|||
} |
|||
|
|||
std::string EncodeBase32(Span<const unsigned char> input, bool pad) |
|||
{ |
|||
static const char *pbase32 = "abcdefghijklmnopqrstuvwxyz234567"; |
|||
|
|||
std::string str; |
|||
str.reserve(((input.size() + 4) / 5) * 8); |
|||
ConvertBits<8, 5, true>([&](int v) { str += pbase32[v]; }, input.begin(), input.end()); |
|||
if (pad) { |
|||
while (str.size() % 8) { |
|||
str += '='; |
|||
} |
|||
} |
|||
return str; |
|||
} |
|||
|
|||
std::string EncodeBase32(const std::string& str, bool pad) |
|||
{ |
|||
return EncodeBase32(MakeUCharSpan(str), pad); |
|||
} |
|||
|
|||
std::vector<unsigned char> DecodeBase32(const char* p, bool* pf_invalid) |
|||
{ |
|||
static const int decode32_table[256] = |
|||
{ |
|||
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, |
|||
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, |
|||
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 26, 27, 28, 29, 30, 31, -1, -1, -1, -1, |
|||
-1, -1, -1, -1, -1, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, |
|||
15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, -1, -1, -1, -1, -1, -1, 0, 1, 2, |
|||
3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, |
|||
23, 24, 25, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, |
|||
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, |
|||
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, |
|||
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, |
|||
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, |
|||
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, |
|||
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 |
|||
}; |
|||
|
|||
const char* e = p; |
|||
std::vector<uint8_t> val; |
|||
val.reserve(strlen(p)); |
|||
while (*p != 0) { |
|||
int x = decode32_table[(unsigned char)*p]; |
|||
if (x == -1) break; |
|||
val.push_back(x); |
|||
++p; |
|||
} |
|||
|
|||
std::vector<unsigned char> ret; |
|||
ret.reserve((val.size() * 5) / 8); |
|||
bool valid = ConvertBits<5, 8, false>([&](unsigned char c) { ret.push_back(c); }, val.begin(), val.end()); |
|||
|
|||
const char* q = p; |
|||
while (valid && *p != 0) { |
|||
if (*p != '=') { |
|||
valid = false; |
|||
break; |
|||
} |
|||
++p; |
|||
} |
|||
valid = valid && (p - e) % 8 == 0 && p - q < 8; |
|||
if (pf_invalid) *pf_invalid = !valid; |
|||
|
|||
return ret; |
|||
} |
|||
|
|||
std::string DecodeBase32(const std::string& str, bool* pf_invalid) |
|||
{ |
|||
if (!ValidAsCString(str)) { |
|||
if (pf_invalid) { |
|||
*pf_invalid = true; |
|||
} |
|||
return {}; |
|||
} |
|||
std::vector<unsigned char> vchRet = DecodeBase32(str.c_str(), pf_invalid); |
|||
return std::string((const char*)vchRet.data(), vchRet.size()); |
|||
} |
|||
|
|||
static bool ParsePrechecks(const std::string& str) |
|||
{ |
|||
if (str.empty()) // No empty string allowed
|
|||
return false; |
|||
if (str.size() >= 1 && (IsSpace(str[0]) || IsSpace(str[str.size()-1]))) // No padding allowed
|
|||
return false; |
|||
if (!ValidAsCString(str)) // No embedded NUL characters allowed
|
|||
return false; |
|||
return true; |
|||
} |
|||
|
|||
bool ParseInt32(const std::string& str, int32_t *out) |
|||
{ |
|||
if (!ParsePrechecks(str)) |
|||
return false; |
|||
char *endp = nullptr; |
|||
errno = 0; // strtol will not set errno if valid
|
|||
long int n = strtol(str.c_str(), &endp, 10); |
|||
if(out) *out = (int32_t)n; |
|||
// Note that strtol returns a *long int*, so even if strtol doesn't report an over/underflow
|
|||
// we still have to check that the returned value is within the range of an *int32_t*. On 64-bit
|
|||
// platforms the size of these types may be different.
|
|||
return endp && *endp == 0 && !errno && |
|||
n >= std::numeric_limits<int32_t>::min() && |
|||
n <= std::numeric_limits<int32_t>::max(); |
|||
} |
|||
|
|||
bool ParseInt64(const std::string& str, int64_t *out) |
|||
{ |
|||
if (!ParsePrechecks(str)) |
|||
return false; |
|||
char *endp = nullptr; |
|||
errno = 0; // strtoll will not set errno if valid
|
|||
long long int n = strtoll(str.c_str(), &endp, 10); |
|||
if(out) *out = (int64_t)n; |
|||
// Note that strtoll returns a *long long int*, so even if strtol doesn't report an over/underflow
|
|||
// we still have to check that the returned value is within the range of an *int64_t*.
|
|||
return endp && *endp == 0 && !errno && |
|||
n >= std::numeric_limits<int64_t>::min() && |
|||
n <= std::numeric_limits<int64_t>::max(); |
|||
} |
|||
|
|||
bool ParseUInt8(const std::string& str, uint8_t *out) |
|||
{ |
|||
uint32_t u32; |
|||
if (!ParseUInt32(str, &u32) || u32 > std::numeric_limits<uint8_t>::max()) { |
|||
return false; |
|||
} |
|||
if (out != nullptr) { |
|||
*out = static_cast<uint8_t>(u32); |
|||
} |
|||
return true; |
|||
} |
|||
|
|||
bool ParseUInt32(const std::string& str, uint32_t *out) |
|||
{ |
|||
if (!ParsePrechecks(str)) |
|||
return false; |
|||
if (str.size() >= 1 && str[0] == '-') // Reject negative values, unfortunately strtoul accepts these by default if they fit in the range
|
|||
return false; |
|||
char *endp = nullptr; |
|||
errno = 0; // strtoul will not set errno if valid
|
|||
unsigned long int n = strtoul(str.c_str(), &endp, 10); |
|||
if(out) *out = (uint32_t)n; |
|||
// Note that strtoul returns a *unsigned long int*, so even if it doesn't report an over/underflow
|
|||
// we still have to check that the returned value is within the range of an *uint32_t*. On 64-bit
|
|||
// platforms the size of these types may be different.
|
|||
return endp && *endp == 0 && !errno && |
|||
n <= std::numeric_limits<uint32_t>::max(); |
|||
} |
|||
|
|||
bool ParseUInt64(const std::string& str, uint64_t *out) |
|||
{ |
|||
if (!ParsePrechecks(str)) |
|||
return false; |
|||
if (str.size() >= 1 && str[0] == '-') // Reject negative values, unfortunately strtoull accepts these by default if they fit in the range
|
|||
return false; |
|||
char *endp = nullptr; |
|||
errno = 0; // strtoull will not set errno if valid
|
|||
unsigned long long int n = strtoull(str.c_str(), &endp, 10); |
|||
if(out) *out = (uint64_t)n; |
|||
// Note that strtoull returns a *unsigned long long int*, so even if it doesn't report an over/underflow
|
|||
// we still have to check that the returned value is within the range of an *uint64_t*.
|
|||
return endp && *endp == 0 && !errno && |
|||
n <= std::numeric_limits<uint64_t>::max(); |
|||
} |
|||
|
|||
|
|||
bool ParseDouble(const std::string& str, double *out) |
|||
{ |
|||
if (!ParsePrechecks(str)) |
|||
return false; |
|||
if (str.size() >= 2 && str[0] == '0' && str[1] == 'x') // No hexadecimal floats allowed
|
|||
return false; |
|||
std::istringstream text(str); |
|||
text.imbue(std::locale::classic()); |
|||
double result; |
|||
text >> result; |
|||
if(out) *out = result; |
|||
return text.eof() && !text.fail(); |
|||
} |
|||
|
|||
std::string FormatParagraph(const std::string& in, size_t width, size_t indent) |
|||
{ |
|||
std::stringstream out; |
|||
size_t ptr = 0; |
|||
size_t indented = 0; |
|||
while (ptr < in.size()) |
|||
{ |
|||
size_t lineend = in.find_first_of('\n', ptr); |
|||
if (lineend == std::string::npos) { |
|||
lineend = in.size(); |
|||
} |
|||
const size_t linelen = lineend - ptr; |
|||
const size_t rem_width = width - indented; |
|||
if (linelen <= rem_width) { |
|||
out << in.substr(ptr, linelen + 1); |
|||
ptr = lineend + 1; |
|||
indented = 0; |
|||
} else { |
|||
size_t finalspace = in.find_last_of(" \n", ptr + rem_width); |
|||
if (finalspace == std::string::npos || finalspace < ptr) { |
|||
// No place to break; just include the entire word and move on
|
|||
finalspace = in.find_first_of("\n ", ptr); |
|||
if (finalspace == std::string::npos) { |
|||
// End of the string, just add it and break
|
|||
out << in.substr(ptr); |
|||
break; |
|||
} |
|||
} |
|||
out << in.substr(ptr, finalspace - ptr) << "\n"; |
|||
if (in[finalspace] == '\n') { |
|||
indented = 0; |
|||
} else if (indent) { |
|||
out << std::string(indent, ' '); |
|||
indented = indent; |
|||
} |
|||
ptr = finalspace + 1; |
|||
} |
|||
} |
|||
return out.str(); |
|||
} |
|||
|
|||
std::string i64tostr(int64_t n) |
|||
{ |
|||
return strprintf("%d", n); |
|||
} |
|||
|
|||
std::string itostr(int n) |
|||
{ |
|||
return strprintf("%d", n); |
|||
} |
|||
|
|||
int64_t atoi64(const char* psz) |
|||
{ |
|||
#ifdef _MSC_VER |
|||
return _atoi64(psz); |
|||
#else |
|||
return strtoll(psz, nullptr, 10); |
|||
#endif |
|||
} |
|||
|
|||
int64_t atoi64(const std::string& str) |
|||
{ |
|||
#ifdef _MSC_VER |
|||
return _atoi64(str.c_str()); |
|||
#else |
|||
return strtoll(str.c_str(), nullptr, 10); |
|||
#endif |
|||
} |
|||
|
|||
int atoi(const std::string& str) |
|||
{ |
|||
return atoi(str.c_str()); |
|||
} |
|||
|
|||
/** Upper bound for mantissa.
|
|||
* 10^18-1 is the largest arbitrary decimal that will fit in a signed 64-bit integer. |
|||
* Larger integers cannot consist of arbitrary combinations of 0-9: |
|||
* |
|||
* 999999999999999999 1^18-1 |
|||
* 9223372036854775807 (1<<63)-1 (max int64_t) |
|||
* 9999999999999999999 1^19-1 (would overflow) |
|||
*/ |
|||
static const int64_t UPPER_BOUND = 1000000000000000000LL - 1LL; |
|||
|
|||
/** Helper function for ParseFixedPoint */ |
|||
static inline bool ProcessMantissaDigit(char ch, int64_t &mantissa, int &mantissa_tzeros) |
|||
{ |
|||
if(ch == '0') |
|||
++mantissa_tzeros; |
|||
else { |
|||
for (int i=0; i<=mantissa_tzeros; ++i) { |
|||
if (mantissa > (UPPER_BOUND / 10LL)) |
|||
return false; /* overflow */ |
|||
mantissa *= 10; |
|||
} |
|||
mantissa += ch - '0'; |
|||
mantissa_tzeros = 0; |
|||
} |
|||
return true; |
|||
} |
|||
|
|||
bool ParseFixedPoint(const std::string &val, int decimals, int64_t *amount_out) |
|||
{ |
|||
int64_t mantissa = 0; |
|||
int64_t exponent = 0; |
|||
int mantissa_tzeros = 0; |
|||
bool mantissa_sign = false; |
|||
bool exponent_sign = false; |
|||
int ptr = 0; |
|||
int end = val.size(); |
|||
int point_ofs = 0; |
|||
|
|||
if (ptr < end && val[ptr] == '-') { |
|||
mantissa_sign = true; |
|||
++ptr; |
|||
} |
|||
if (ptr < end) |
|||
{ |
|||
if (val[ptr] == '0') { |
|||
/* pass single 0 */ |
|||
++ptr; |
|||
} else if (val[ptr] >= '1' && val[ptr] <= '9') { |
|||
while (ptr < end && IsDigit(val[ptr])) { |
|||
if (!ProcessMantissaDigit(val[ptr], mantissa, mantissa_tzeros)) |
|||
return false; /* overflow */ |
|||
++ptr; |
|||
} |
|||
} else return false; /* missing expected digit */ |
|||
} else return false; /* empty string or loose '-' */ |
|||
if (ptr < end && val[ptr] == '.') |
|||
{ |
|||
++ptr; |
|||
if (ptr < end && IsDigit(val[ptr])) |
|||
{ |
|||
while (ptr < end && IsDigit(val[ptr])) { |
|||
if (!ProcessMantissaDigit(val[ptr], mantissa, mantissa_tzeros)) |
|||
return false; /* overflow */ |
|||
++ptr; |
|||
++point_ofs; |
|||
} |
|||
} else return false; /* missing expected digit */ |
|||
} |
|||
if (ptr < end && (val[ptr] == 'e' || val[ptr] == 'E')) |
|||
{ |
|||
++ptr; |
|||
if (ptr < end && val[ptr] == '+') |
|||
++ptr; |
|||
else if (ptr < end && val[ptr] == '-') { |
|||
exponent_sign = true; |
|||
++ptr; |
|||
} |
|||
if (ptr < end && IsDigit(val[ptr])) { |
|||
while (ptr < end && IsDigit(val[ptr])) { |
|||
if (exponent > (UPPER_BOUND / 10LL)) |
|||
return false; /* overflow */ |
|||
exponent = exponent * 10 + val[ptr] - '0'; |
|||
++ptr; |
|||
} |
|||
} else return false; /* missing expected digit */ |
|||
} |
|||
if (ptr != end) |
|||
return false; /* trailing garbage */ |
|||
|
|||
/* finalize exponent */ |
|||
if (exponent_sign) |
|||
exponent = -exponent; |
|||
exponent = exponent - point_ofs + mantissa_tzeros; |
|||
|
|||
/* finalize mantissa */ |
|||
if (mantissa_sign) |
|||
mantissa = -mantissa; |
|||
|
|||
/* convert to one 64-bit fixed-point value */ |
|||
exponent += decimals; |
|||
if (exponent < 0) |
|||
return false; /* cannot represent values smaller than 10^-decimals */ |
|||
if (exponent >= 18) |
|||
return false; /* cannot represent values larger than or equal to 10^(18-decimals) */ |
|||
|
|||
for (int i=0; i < exponent; ++i) { |
|||
if (mantissa > (UPPER_BOUND / 10LL) || mantissa < -(UPPER_BOUND / 10LL)) |
|||
return false; /* overflow */ |
|||
mantissa *= 10; |
|||
} |
|||
if (mantissa > UPPER_BOUND || mantissa < -UPPER_BOUND) |
|||
return false; /* overflow */ |
|||
|
|||
if (amount_out) |
|||
*amount_out = mantissa; |
|||
|
|||
return true; |
|||
} |
|||
|
|||
std::string ToLower(const std::string& str) |
|||
{ |
|||
std::string r; |
|||
for (auto ch : str) r += ToLower((unsigned char)ch); |
|||
return r; |
|||
} |
|||
|
|||
std::string ToUpper(const std::string& str) |
|||
{ |
|||
std::string r; |
|||
for (auto ch : str) r += ToUpper((unsigned char)ch); |
|||
return r; |
|||
} |
|||
|
|||
std::string Capitalize(std::string str) |
|||
{ |
|||
if (str.empty()) return str; |
|||
str[0] = ToUpper(str.front()); |
|||
return str; |
|||
} |
|||
|
|||
std::string HexStr(const Span<const uint8_t> s) |
|||
{ |
|||
std::string rv; |
|||
static constexpr char hexmap[16] = { '0', '1', '2', '3', '4', '5', '6', '7', |
|||
'8', '9', 'a', 'b', 'c', 'd', 'e', 'f' }; |
|||
rv.reserve(s.size() * 2); |
|||
for (uint8_t v: s) { |
|||
rv.push_back(hexmap[v >> 4]); |
|||
rv.push_back(hexmap[v & 15]); |
|||
} |
|||
return rv; |
|||
} |
@ -0,0 +1,302 @@ |
|||
// Copyright (c) 2009-2010 Satoshi Nakamoto
|
|||
// Copyright (c) 2009-2020 The Bitcoin Core developers
|
|||
// Copyright (c) 2016-2022 The Hush developers
|
|||
// Distributed under the GPLv3 software license, see the accompanying
|
|||
// file COPYING or https://www.gnu.org/licenses/gpl-3.0.en.html
|
|||
|
|||
/**
|
|||
* Utilities for converting data from/to strings. |
|||
*/ |
|||
#ifndef BITCOIN_UTIL_STRENCODINGS_H |
|||
#define BITCOIN_UTIL_STRENCODINGS_H |
|||
|
|||
#include "attributes.h" |
|||
#include "span.h" |
|||
|
|||
#include <cstdint> |
|||
#include <iterator> |
|||
#include <string> |
|||
#include <vector> |
|||
|
|||
#define BEGIN(a) ((char*)&(a)) |
|||
#define END(a) ((char*)&((&(a))[1])) |
|||
#define UBEGIN(a) ((unsigned char*)&(a)) |
|||
#define UEND(a) ((unsigned char*)&((&(a))[1])) |
|||
#define ARRAYLEN(array) (sizeof(array)/sizeof((array)[0])) |
|||
|
|||
/** This is needed because the foreach macro can't get over the comma in pair<t1, t2> */ |
|||
#define PAIRTYPE(t1, t2) std::pair<t1, t2> |
|||
|
|||
/** Used by SanitizeString() */ |
|||
enum SafeChars |
|||
{ |
|||
SAFE_CHARS_DEFAULT, //!< The full set of allowed chars
|
|||
SAFE_CHARS_UA_COMMENT, //!< BIP-0014 subset
|
|||
SAFE_CHARS_FILENAME, //!< Chars allowed in filenames
|
|||
SAFE_CHARS_URI, //!< Chars allowed in URIs (RFC 3986)
|
|||
}; |
|||
|
|||
std::string SanitizeFilename(const std::string& str); |
|||
/**
|
|||
* Remove unsafe chars. Safe chars chosen to allow simple messages/URLs/email |
|||
* addresses, but avoid anything even possibly remotely dangerous like & or > |
|||
* @param[in] str The string to sanitize |
|||
* @param[in] rule The set of safe chars to choose (default: least restrictive) |
|||
* @return A new string without unsafe chars |
|||
*/ |
|||
std::string SanitizeString(const std::string& str, int rule = SAFE_CHARS_DEFAULT); |
|||
std::string HexInt(uint32_t val); |
|||
uint32_t ParseHexToUInt32(const std::string& str); |
|||
std::vector<unsigned char> ParseHex(const char* psz); |
|||
std::vector<unsigned char> ParseHex(const std::string& str); |
|||
signed char HexDigit(char c); |
|||
/* Returns true if each character in str is a hex character, and has an even
|
|||
* number of hex digits.*/ |
|||
bool IsHex(const std::string& str); |
|||
/**
|
|||
* Return true if the string is a hex number, optionally prefixed with "0x" |
|||
*/ |
|||
bool IsHexNumber(const std::string& str); |
|||
std::vector<unsigned char> DecodeBase64(const char* p, bool* pf_invalid = nullptr); |
|||
std::string DecodeBase64(const std::string& str, bool* pf_invalid = nullptr); |
|||
std::string EncodeBase64(Span<const unsigned char> input); |
|||
std::string EncodeBase64(const unsigned char* pch, size_t len); |
|||
std::string EncodeBase64(const std::string& str); |
|||
std::vector<unsigned char> DecodeBase32(const char* p, bool* pf_invalid = nullptr); |
|||
std::string DecodeBase32(const std::string& str, bool* pf_invalid = nullptr); |
|||
|
|||
/**
|
|||
* Base32 encode. |
|||
* If `pad` is true, then the output will be padded with '=' so that its length |
|||
* is a multiple of 8. |
|||
*/ |
|||
std::string EncodeBase32(Span<const unsigned char> input, bool pad = true); |
|||
|
|||
/**
|
|||
* Base32 encode. |
|||
* If `pad` is true, then the output will be padded with '=' so that its length |
|||
* is a multiple of 8. |
|||
*/ |
|||
std::string EncodeBase32(const std::string& str, bool pad = true); |
|||
|
|||
void SplitHostPort(std::string in, int& portOut, std::string& hostOut); |
|||
std::string i64tostr(int64_t n); |
|||
std::string itostr(int n); |
|||
int64_t atoi64(const char* psz); |
|||
int64_t atoi64(const std::string& str); |
|||
int atoi(const std::string& str); |
|||
|
|||
/**
|
|||
* Tests if the given character is a decimal digit. |
|||
* @param[in] c character to test |
|||
* @return true if the argument is a decimal digit; otherwise false. |
|||
*/ |
|||
constexpr bool IsDigit(char c) |
|||
{ |
|||
return c >= '0' && c <= '9'; |
|||
} |
|||
|
|||
/**
|
|||
* Tests if the given character is a whitespace character. The whitespace characters |
|||
* are: space, form-feed ('\f'), newline ('\n'), carriage return ('\r'), horizontal |
|||
* tab ('\t'), and vertical tab ('\v'). |
|||
* |
|||
* This function is locale independent. Under the C locale this function gives the |
|||
* same result as std::isspace. |
|||
* |
|||
* @param[in] c character to test |
|||
* @return true if the argument is a whitespace character; otherwise false |
|||
*/ |
|||
constexpr inline bool IsSpace(char c) noexcept { |
|||
return c == ' ' || c == '\f' || c == '\n' || c == '\r' || c == '\t' || c == '\v'; |
|||
} |
|||
|
|||
/**
|
|||
* Convert string to signed 32-bit integer with strict parse error feedback. |
|||
* @returns true if the entire string could be parsed as valid integer, |
|||
* false if not the entire string could be parsed or when overflow or underflow occurred. |
|||
*/ |
|||
bool ParseInt32(const std::string& str, int32_t *out); |
|||
|
|||
/**
|
|||
* Convert string to signed 64-bit integer with strict parse error feedback. |
|||
* @returns true if the entire string could be parsed as valid integer, |
|||
* false if not the entire string could be parsed or when overflow or underflow occurred. |
|||
*/ |
|||
bool ParseInt64(const std::string& str, int64_t *out); |
|||
|
|||
/**
|
|||
* Convert decimal string to unsigned 8-bit integer with strict parse error feedback. |
|||
* @returns true if the entire string could be parsed as valid integer, |
|||
* false if not the entire string could be parsed or when overflow or underflow occurred. |
|||
*/ |
|||
bool ParseUInt8(const std::string& str, uint8_t *out); |
|||
|
|||
/**
|
|||
* Convert decimal string to unsigned 32-bit integer with strict parse error feedback. |
|||
* @returns true if the entire string could be parsed as valid integer, |
|||
* false if not the entire string could be parsed or when overflow or underflow occurred. |
|||
*/ |
|||
bool ParseUInt32(const std::string& str, uint32_t *out); |
|||
|
|||
/**
|
|||
* Convert decimal string to unsigned 64-bit integer with strict parse error feedback. |
|||
* @returns true if the entire string could be parsed as valid integer, |
|||
* false if not the entire string could be parsed or when overflow or underflow occurred. |
|||
*/ |
|||
bool ParseUInt64(const std::string& str, uint64_t *out); |
|||
|
|||
/**
|
|||
* Convert string to double with strict parse error feedback. |
|||
* @returns true if the entire string could be parsed as valid double, |
|||
* false if not the entire string could be parsed or when overflow or underflow occurred. |
|||
*/ |
|||
bool ParseDouble(const std::string& str, double *out); |
|||
|
|||
template<typename T> |
|||
std::string HexStr(const T itbegin, const T itend, bool fSpaces=false) |
|||
{ |
|||
std::string rv; |
|||
static const char hexmap[16] = { '0', '1', '2', '3', '4', '5', '6', '7', |
|||
'8', '9', 'a', 'b', 'c', 'd', 'e', 'f' }; |
|||
rv.reserve((itend-itbegin)*3); |
|||
for(T it = itbegin; it < itend; ++it) |
|||
{ |
|||
unsigned char val = (unsigned char)(*it); |
|||
if(fSpaces && it != itbegin) |
|||
rv.push_back(' '); |
|||
rv.push_back(hexmap[val>>4]); |
|||
rv.push_back(hexmap[val&15]); |
|||
} |
|||
|
|||
return rv; |
|||
} |
|||
|
|||
template<typename T> |
|||
inline std::string HexStr(const T& vch, bool fSpaces=false) |
|||
{ |
|||
return HexStr(vch.begin(), vch.end(), fSpaces); |
|||
} |
|||
|
|||
/**
|
|||
* Convert a span of bytes to a lower-case hexadecimal string. |
|||
*/ |
|||
std::string HexStr(const Span<const uint8_t> s); |
|||
inline std::string HexStr(const Span<const char> s) { return HexStr(MakeUCharSpan(s)); } |
|||
|
|||
/**
|
|||
* Format a paragraph of text to a fixed width, adding spaces for |
|||
* indentation to any added line. |
|||
*/ |
|||
std::string FormatParagraph(const std::string& in, size_t width = 79, size_t indent = 0); |
|||
|
|||
/**
|
|||
* Timing-attack-resistant comparison. |
|||
* Takes time proportional to length |
|||
* of first argument. |
|||
*/ |
|||
template <typename T> |
|||
bool TimingResistantEqual(const T& a, const T& b) |
|||
{ |
|||
if (b.size() == 0) return a.size() == 0; |
|||
size_t accumulator = a.size() ^ b.size(); |
|||
for (size_t i = 0; i < a.size(); i++) |
|||
accumulator |= a[i] ^ b[i%b.size()]; |
|||
return accumulator == 0; |
|||
} |
|||
|
|||
/** Parse number as fixed point according to JSON number syntax.
|
|||
* See http://json.org/number.gif
|
|||
* @returns true on success, false on error. |
|||
* @note The result must be in the range (-10^18,10^18), otherwise an overflow error will trigger. |
|||
*/ |
|||
bool ParseFixedPoint(const std::string &val, int decimals, int64_t *amount_out); |
|||
|
|||
/** Convert from one power-of-2 number base to another. */ |
|||
template<int frombits, int tobits, bool pad, typename O, typename I> |
|||
bool ConvertBits(const O& outfn, I it, I end) { |
|||
size_t acc = 0; |
|||
size_t bits = 0; |
|||
constexpr size_t maxv = (1 << tobits) - 1; |
|||
constexpr size_t max_acc = (1 << (frombits + tobits - 1)) - 1; |
|||
while (it != end) { |
|||
acc = ((acc << frombits) | *it) & max_acc; |
|||
bits += frombits; |
|||
while (bits >= tobits) { |
|||
bits -= tobits; |
|||
outfn((acc >> bits) & maxv); |
|||
} |
|||
++it; |
|||
} |
|||
if (pad) { |
|||
if (bits) outfn((acc << (tobits - bits)) & maxv); |
|||
} else if (bits >= frombits || ((acc << (tobits - bits)) & maxv)) { |
|||
return false; |
|||
} |
|||
return true; |
|||
} |
|||
|
|||
/**
|
|||
* Converts the given character to its lowercase equivalent. |
|||
* This function is locale independent. It only converts uppercase |
|||
* characters in the standard 7-bit ASCII range. |
|||
* This is a feature, not a limitation. |
|||
* |
|||
* @param[in] c the character to convert to lowercase. |
|||
* @return the lowercase equivalent of c; or the argument |
|||
* if no conversion is possible. |
|||
*/ |
|||
constexpr char ToLower(char c) |
|||
{ |
|||
return (c >= 'A' && c <= 'Z' ? (c - 'A') + 'a' : c); |
|||
} |
|||
|
|||
/**
|
|||
* Returns the lowercase equivalent of the given string. |
|||
* This function is locale independent. It only converts uppercase |
|||
* characters in the standard 7-bit ASCII range. |
|||
* This is a feature, not a limitation. |
|||
* |
|||
* @param[in] str the string to convert to lowercase. |
|||
* @returns lowercased equivalent of str |
|||
*/ |
|||
std::string ToLower(const std::string& str); |
|||
|
|||
/**
|
|||
* Converts the given character to its uppercase equivalent. |
|||
* This function is locale independent. It only converts lowercase |
|||
* characters in the standard 7-bit ASCII range. |
|||
* This is a feature, not a limitation. |
|||
* |
|||
* @param[in] c the character to convert to uppercase. |
|||
* @return the uppercase equivalent of c; or the argument |
|||
* if no conversion is possible. |
|||
*/ |
|||
constexpr char ToUpper(char c) |
|||
{ |
|||
return (c >= 'a' && c <= 'z' ? (c - 'a') + 'A' : c); |
|||
} |
|||
|
|||
/**
|
|||
* Returns the uppercase equivalent of the given string. |
|||
* This function is locale independent. It only converts lowercase |
|||
* characters in the standard 7-bit ASCII range. |
|||
* This is a feature, not a limitation. |
|||
* |
|||
* @param[in] str the string to convert to uppercase. |
|||
* @returns UPPERCASED EQUIVALENT OF str |
|||
*/ |
|||
std::string ToUpper(const std::string& str); |
|||
|
|||
/**
|
|||
* Capitalizes the first character of the given string. |
|||
* This function is locale independent. It only converts lowercase |
|||
* characters in the standard 7-bit ASCII range. |
|||
* This is a feature, not a limitation. |
|||
* |
|||
* @param[in] str the string to capitalize. |
|||
* @returns string with the first letter capitalized. |
|||
*/ |
|||
std::string Capitalize(std::string str); |
|||
|
|||
#endif // BITCOIN_UTIL_STRENCODINGS_H
|
@ -0,0 +1,6 @@ |
|||
// Copyright (c) 2019 The Bitcoin Core developers
|
|||
// Copyright (c) 2016-2022 The Hush developers
|
|||
// Distributed under the GPLv3 software license, see the accompanying
|
|||
// file COPYING or https://www.gnu.org/licenses/gpl-3.0.en.html
|
|||
|
|||
#include "util/string.h" |
@ -0,0 +1,99 @@ |
|||
// Copyright (c) 2019-2020 The Bitcoin Core developers
|
|||
// Copyright (c) 2016-2022 The Hush developers
|
|||
// Distributed under the GPLv3 software license, see the accompanying
|
|||
// file COPYING or https://www.gnu.org/licenses/gpl-3.0.en.html
|
|||
|
|||
#ifndef BITCOIN_UTIL_STRING_H |
|||
#define BITCOIN_UTIL_STRING_H |
|||
|
|||
#include "attributes.h" |
|||
|
|||
#include <algorithm> |
|||
#include <array> |
|||
#include <cstring> |
|||
#include <locale> |
|||
#include <sstream> |
|||
#include <string> |
|||
#include <vector> |
|||
|
|||
[[nodiscard]] inline std::string TrimString(const std::string& str, const std::string& pattern = " \f\n\r\t\v") |
|||
{ |
|||
std::string::size_type front = str.find_first_not_of(pattern); |
|||
if (front == std::string::npos) { |
|||
return std::string(); |
|||
} |
|||
std::string::size_type end = str.find_last_not_of(pattern); |
|||
return str.substr(front, end - front + 1); |
|||
} |
|||
|
|||
[[nodiscard]] inline std::string RemovePrefix(const std::string& str, const std::string& prefix) |
|||
{ |
|||
if (str.substr(0, prefix.size()) == prefix) { |
|||
return str.substr(prefix.size()); |
|||
} |
|||
return str; |
|||
} |
|||
|
|||
/**
|
|||
* Join a list of items |
|||
* |
|||
* @param list The list to join |
|||
* @param separator The separator |
|||
* @param unary_op Apply this operator to each item in the list |
|||
*/ |
|||
template <typename T, typename BaseType, typename UnaryOp> |
|||
auto Join(const std::vector<T>& list, const BaseType& separator, UnaryOp unary_op) |
|||
-> decltype(unary_op(list.at(0))) |
|||
{ |
|||
decltype(unary_op(list.at(0))) ret; |
|||
for (size_t i = 0; i < list.size(); ++i) { |
|||
if (i > 0) ret += separator; |
|||
ret += unary_op(list.at(i)); |
|||
} |
|||
return ret; |
|||
} |
|||
|
|||
template <typename T> |
|||
T Join(const std::vector<T>& list, const T& separator) |
|||
{ |
|||
return Join(list, separator, [](const T& i) { return i; }); |
|||
} |
|||
|
|||
// Explicit overload needed for c_str arguments, which would otherwise cause a substitution failure in the template above.
|
|||
inline std::string Join(const std::vector<std::string>& list, const std::string& separator) |
|||
{ |
|||
return Join<std::string>(list, separator); |
|||
} |
|||
|
|||
/**
|
|||
* Check if a string does not contain any embedded NUL (\0) characters |
|||
*/ |
|||
[[nodiscard]] inline bool ValidAsCString(const std::string& str) noexcept |
|||
{ |
|||
return str.size() == strlen(str.c_str()); |
|||
} |
|||
|
|||
/**
|
|||
* Locale-independent version of std::to_string |
|||
*/ |
|||
template <typename T> |
|||
std::string ToString(const T& t) |
|||
{ |
|||
std::ostringstream oss; |
|||
oss.imbue(std::locale::classic()); |
|||
oss << t; |
|||
return oss.str(); |
|||
} |
|||
|
|||
/**
|
|||
* Check whether a container begins with the given prefix. |
|||
*/ |
|||
template <typename T1, size_t PREFIX_LEN> |
|||
[[nodiscard]] inline bool HasPrefix(const T1& obj, |
|||
const std::array<uint8_t, PREFIX_LEN>& prefix) |
|||
{ |
|||
return obj.size() >= PREFIX_LEN && |
|||
std::equal(std::begin(prefix), std::end(prefix), std::begin(obj)); |
|||
} |
|||
|
|||
#endif // BITCOIN_UTIL_STRENCODINGS_H
|
Loading…
Reference in new issue