Compare commits

...

241 Commits
0.6.0 ... 0.8.0

Author SHA1 Message Date
orignal
e002c05350 Merge branch 'master' of https://github.com/PurpleI2P/i2pd 2015-02-22 17:07:42 -05:00
orignal
6401ed71ae Update version.h
0.8.0
2015-02-22 17:05:52 -05:00
orignal
621bfde961 handle incoming connection failure 2015-02-22 17:04:42 -05:00
orignal
5b5c06179c original_at_mail.i2p.crt added 2015-02-22 09:12:43 -05:00
orignal
b187babd20 128 bytes key expansion 2015-02-21 22:07:55 -05:00
orignal
05e49bbeab show SAM sessions through web interface 2015-02-20 22:47:36 -05:00
orignal
c88c6a9b63 include https hosts to reseeder's list 2015-02-20 15:15:23 -05:00
orignal
8e795cc2aa include https hosts to reseeder's list 2015-02-20 15:11:00 -05:00
orignal
ad5bac6598 ClientKeyExchange length depend on key length from ceritifcate 2015-02-20 14:26:49 -05:00
orignal
0d468a8f48 extract https content 2015-02-20 12:21:33 -05:00
orignal
71dae29077 send and receive data 2015-02-20 10:47:44 -05:00
orignal
d1b26b72e3 proper handshake messages hash calculations 2015-02-19 22:13:41 -05:00
orignal
64a4799c8c fixed incorrect MAC calculation 2015-02-19 16:35:07 -05:00
orignal
e5d9c26868 use 256 bytes block for keys expansion 2015-02-19 13:50:04 -05:00
orignal
5deccd7833 calculate MAC 2015-02-19 11:26:37 -05:00
orignal
e1f64e2476 moved https code to TlsSession 2015-02-19 10:06:11 -05:00
orignal
cf5499375e encrypt finishes message 2015-02-18 22:25:30 -05:00
orignal
a3736fc06e tls encrypt and decrypt 2015-02-18 16:52:00 -05:00
orignal
68a03c2134 send ChangeCipherSpecs 2015-02-18 11:44:32 -05:00
orignal
dce8cf1af2 tls 1.2 and RSA_WITH_AES_256_CBC_SHA256 2015-02-18 10:28:29 -05:00
orignal
29d118a19a hanshakes hash and finishes message 2015-02-17 22:45:55 -05:00
orignal
ec50b97aa8 reduced amount of logging 2015-02-17 19:14:31 -05:00
orignal
fbf672288f MAC and encryption keys 2015-02-17 16:50:37 -05:00
orignal
47e8cfd91e calculate master secret 2015-02-17 15:34:30 -05:00
orignal
9968485cdd send ClientKeyExchange 2015-02-16 21:28:37 -05:00
orignal
6ea037cc47 process server certificate 2015-02-16 11:21:33 -05:00
orignal
c5f0be126e client hello for HTTPS 2015-02-15 23:03:04 -05:00
8db34a4d7f Merge branch 'master' of github.com:PurpleI2P/i2pd 2015-02-15 19:20:41 +00:00
732ff67076 New certificate; will be switched on the server for a future release. 2015-02-15 19:19:09 +00:00
orignal
e3190a4ca9 SSU data receive batching 2015-02-15 14:17:55 -05:00
f2c849703a Remove reseed host, no longer in use 2015-02-15 19:17:41 +00:00
orignal
e901307d8d fixed crash 2015-02-15 10:23:06 -05:00
orignal
e038467c89 unique_ptr for sent and incomplete messages 2015-02-14 22:24:10 -05:00
orignal
776582e019 fixed typo 2015-02-14 21:00:40 -05:00
orignal
018daa8837 catch sendto exception 2015-02-14 17:23:15 -05:00
orignal
10b733d215 don't throw exception 2015-02-14 17:20:21 -05:00
orignal
97ec65cccd read all sections from tunnels.cfg 2015-02-13 19:00:17 -05:00
orignal
d2d7b3b348 multiple server I2P tunnels 2015-02-13 15:56:57 -05:00
orignal
b0052eae05 doesn't create same local destination twice 2015-02-13 14:55:48 -05:00
orignal
ed692ffba3 make sure only one I2P tunnel per local prot 2015-02-13 10:18:42 -05:00
orignal
242bc3624a read tunnels.cfg 2015-02-12 22:20:07 -05:00
orignal
18121a99ca config file for I2PTunnels 2015-02-12 16:11:56 -05:00
orignal
93c2c13f96 fixed memory leak 2015-02-12 14:03:59 -05:00
orignal
b11fd250c1 fixed race condition 2015-02-12 11:40:42 -05:00
orignal
93857b690a fixed hand at shutdown 2015-02-11 22:48:26 -05:00
orignal
34d88db0b2 Merge pull request #158 from multikatt/master
typo: stoped->stopped
2015-02-11 22:10:07 -05:00
multikatt
dcad550a2f typo: stoped->stopped 2015-02-11 21:06:52 -05:00
orignal
816e4d533d fixed race condition 2015-02-11 18:47:20 -05:00
orignal
53adf7a793 cleanup dead peers 2015-02-11 14:45:25 -05:00
orignal
0ed9dc4e93 complete session if presented 2015-02-10 17:36:39 -05:00
orignal
9eba7923a7 use insert instead [] 2015-02-10 16:51:47 -05:00
orignal
3263f6fefc ban abusing ipv6 addresses 2015-02-10 15:54:07 -05:00
orignal
a27dd17cef fixed memory leak 2015-02-10 15:51:56 -05:00
orignal
52f9d5f0aa ban abusing IPs 2015-02-10 13:05:08 -05:00
orignal
6783b22ad0 terminate non-responding NTCP sessions by timeout 2015-02-10 10:54:04 -05:00
orignal
a3c1c314d0 don't insert same transit tunnel twice 2015-02-10 09:39:49 -05:00
orignal
d01f3b094b allow netDb cleanup after every 500 messages 2015-02-10 09:30:48 -05:00
orignal
8492e87d29 fxied race condition 2015-02-09 22:19:29 -05:00
orignal
923b64eb48 delete messages sent to disconnected session 2015-02-09 16:33:52 -05:00
orignal
89883e2078 don't look for session if a packet is from same endpoint as previous 2015-02-09 14:50:19 -05:00
orignal
23907e6cb1 don't look for session if a packet is from same endpoint as previous 2015-02-09 14:48:01 -05:00
orignal
c6c414c382 receive multiple UDP packets 2015-02-09 11:53:11 -05:00
orignal
c349652a27 terminate receivers 2015-02-08 21:12:38 -05:00
orignal
f9731a76ec separate receivers thread 2015-02-08 20:28:18 -05:00
orignal
7dd159add2 use 'available' method 2015-02-08 20:27:17 -05:00
orignal
1ad04bf8bf read more data from socket if available 2015-02-08 11:52:03 -05:00
6064d12af3 Sync reseed servers with the java router
contrib/certificates/ssl/netdb.i2p2.no2.crt will be needed
when netdb.i2p2.no updates to a stronger certificate later this year
2015-02-08 16:27:01 +00:00
orignal
9b7820a09d clean obsolete SSU data 2015-02-08 09:15:13 -05:00
orignal
e5fdee272b clean obsolete SSU data 2015-02-08 08:50:05 -05:00
orignal
a80e4ef0ea fixed memory leak 2015-02-07 17:58:29 -05:00
orignal
ab0bd908ec fixed race condition 2015-02-07 15:25:06 -05:00
orignal
9c7fcfbe3f Merge pull request #157 from klondi/httpproxy
Get Jump services working again although, at what price!
2015-02-07 13:34:31 -05:00
Francisco Blas (klondike) Izquierdo Riera
1bbaa5ba22 URLdecode the base64 part of the key 2015-02-07 18:34:25 +01:00
Francisco Blas (klondike) Izquierdo Riera
b22423e2d5 Get Jump services working again although, at what price\! 2015-02-07 17:57:16 +01:00
orignal
5730b15f01 fixed race condition 2015-02-06 20:53:48 -05:00
orignal
8305fd5f82 check if connection to peer exists already 2015-02-06 18:37:37 -05:00
orignal
0518b08ca6 drop second incoming connection with same identity 2015-02-06 16:03:47 -05:00
orignal
8e75d8c39a check accepted socket for error 2015-02-06 13:49:00 -05:00
orignal
6beb527058 teminate non-connected NTCP session 2015-02-06 11:14:41 -05:00
orignal
b9e3931e80 use shared_ptr for inbound tunnels 2015-02-05 18:53:43 -05:00
orignal
7945126e86 use unique_ptr for requested destination 2015-02-05 13:36:28 -05:00
orignal
408a67f34f use unique_ptr for incomplete message 2015-02-05 12:13:37 -05:00
orignal
0dac2a74d3 check for duplicate msgID 2015-02-04 22:16:44 -05:00
orignal
9896570e32 cleanup from extra log messages 2015-02-04 22:05:09 -05:00
orignal
9639ab7f1e fixed memory leak 2015-02-04 21:24:48 -05:00
orignal
778d1afda0 cleand destination requests every 15 seconds 2015-02-04 15:34:52 -05:00
orignal
02c22b850b fixed compilation bug for boost < 1.49 and boost 1.49 with gcc 4.7 2015-02-04 11:08:26 -05:00
orignal
bd035e1c3d fixed build for boost below 1.49 2015-02-04 09:40:00 -05:00
orignal
b923b1e31d Update version.h
release 0.7.0
2015-02-03 20:59:12 -05:00
orignal
14f448f4c7 show tunnels queue size 2015-02-03 16:45:19 -05:00
orignal
cd8e9e59fa don't request same RouterInfo twice 2015-02-03 16:14:33 -05:00
orignal
4a6847da8d RTT 2015-02-03 13:46:44 -05:00
orignal
c5a3832eae handle exploratory lookups 2015-02-02 22:34:55 -05:00
orignal
7f75d250c7 Merge pull request #151 from ygrishin/master
fixed HTTP connection being reset on Windows
2015-02-02 21:58:53 -05:00
orignal
95dbc20350 log more disgnostics data 2015-02-02 20:15:49 -05:00
orignal
f846b87590 make sure DeliveryStatus and DatabseStore are sent first 2015-02-02 16:08:35 -05:00
orignal
e3764bef37 fixed incorect reply data parsing for DatabaseStore 2015-02-02 13:06:02 -05:00
orignal
d7c5c24ce4 pass ident hash by value to transports thread 2015-02-02 12:01:21 -05:00
orignal
60ba7be319 floodfill parameter 2015-02-02 11:15:38 -05:00
orignal
2215dd7bd7 ability to turn floodfill off 2015-02-02 11:14:09 -05:00
orignal
07ce9c41bf DatabaseLookup flags 2015-02-02 11:06:36 -05:00
orignal
24564f1faa moved UPNP to common files 2015-02-02 09:58:34 -05:00
orignal
ba11971513 don't delete updated RouterInfo 2015-02-02 09:40:03 -05:00
ygrishin
803737011a fixed HTTP connection being reset on Windows 2015-02-01 22:24:47 -07:00
orignal
8ad9f2681c send 3 closest floodfills 2015-02-01 19:58:26 -05:00
orignal
b46c3036d8 flood 2015-02-01 18:30:27 -05:00
orignal
02ed7d6059 fixed non-responding destination request 2015-02-01 16:18:08 -05:00
orignal
2c2acae50d invoke acceptor on reset 2015-02-01 09:34:32 -05:00
orignal
ac438fbd7d use stream buffer for sending identity 2015-01-31 21:49:54 -05:00
orignal
feffbc9330 fixed typo 2015-01-31 10:39:29 -05:00
orignal
908404ab62 show send buffer size 2015-01-30 21:41:32 -05:00
orignal
618abd6320 use unique_ptr for sent fragments 2015-01-30 20:30:39 -05:00
orignal
79087f6942 detect congesion for first message in sned queue only 2015-01-30 16:43:31 -05:00
orignal
9a43f0d54c don't send DatabaseStore reply for local destinations 2015-01-30 15:29:33 -05:00
orignal
79af7c22d9 send DatabaseStore reply 2015-01-30 15:13:09 -05:00
orignal
0b911a5caa use I2NPMessagesHandler for SSU 2015-01-29 22:35:57 -05:00
orignal
aae837f642 congesion control during retransmission 2015-01-29 19:17:44 -05:00
orignal
5887e8c8c4 slow start and congestion avoidance 2015-01-29 15:34:43 -05:00
orignal
974a7ff3f5 shared_ptr for RoutingDestination 2015-01-28 21:37:08 -05:00
orignal
938fa00469 publishing with flood 2015-01-28 21:01:16 -05:00
orignal
679faf5149 specify reply token for RIs DatabaseStore 2015-01-28 16:16:25 -05:00
orignal
7e45233c7d floodfill parameter 2015-01-28 15:12:15 -05:00
orignal
2ed69ef602 clean up expired LeaseSets 2015-01-28 14:20:28 -05:00
orignal
192a08b5bf check tunnel status instead fidning it every time 2015-01-27 22:31:57 -05:00
orignal
763547f465 fixed corrupted NTCP messages 2015-01-27 19:12:27 -05:00
orignal
b3e08b2cf4 shared_ptr for tunnels 2015-01-27 14:55:46 -05:00
orignal
d1d6797d3e store LeaseSet as shared_ptr 2015-01-27 11:27:58 -05:00
orignal
4b094b2156 handle tunnel build messages in tunnels thread 2015-01-26 20:49:16 -05:00
orignal
562cdc12d1 check message type of follow on message 2015-01-26 14:48:24 -05:00
orignal
8f562215b0 separate inbound and outbound pending tunnels 2015-01-26 11:56:10 -05:00
orignal
8a478e4616 check for max number of NACKs 2015-01-25 22:01:09 -05:00
orignal
f75de6af82 window 2015-01-25 17:43:34 -05:00
orignal
a061f3339e use send buffer for a stream 2015-01-25 16:18:26 -05:00
orignal
724c417f09 fixed typo 2015-01-25 11:43:27 -05:00
orignal
9c23d03d8d fixed typo 2015-01-25 10:05:50 -05:00
orignal
b8740c008b fixed crash 2015-01-25 10:05:19 -05:00
orignal
588c613043 naming lookup of .b32 address through LeaseSet request 2015-01-24 15:34:46 -05:00
orignal
1dc166f0f8 transit tunnel gateway batching 2015-01-23 22:05:33 -05:00
orignal
82103e6a39 process TunnelGateway message in tunnel thread 2015-01-23 16:26:39 -05:00
orignal
33bce67a4e Update copyright
name changed to PurpleI2P
2015-01-23 14:03:41 -05:00
orignal
aeb3c8b8b9 Update LICENSE
name changed to PurpleI2P
2015-01-23 14:02:37 -05:00
orignal
24c00b0985 schedule routing session cleanup 2015-01-23 12:48:25 -05:00
orignal
a25646a129 cleanup routing sessions 2015-01-23 10:07:11 -05:00
orignal
0c73aff0a2 I2NPMessagesHandler 2015-01-22 22:00:41 -05:00
orignal
3ed1fee7ce remove stream on close 2015-01-22 19:02:28 -05:00
orignal
b269bda52b shared_ptr for GarlicRouting Session 2015-01-22 15:31:34 -05:00
orignal
2ab0ff8aea TransitTunnelParticipant 2015-01-21 21:50:46 -05:00
orignal
46a36f766f don't restart subscriptions update timer if no subscription presented 2015-01-21 16:34:50 -05:00
orignal
276d5097c1 fixed 'bad descriptor' exception 2015-01-21 15:59:05 -05:00
orignal
ec980edf56 don't look for tunnel again if tunnelID is the same as for previous message 2015-01-21 15:13:46 -05:00
orignal
89dead79c4 common HandleTunnelData for own and transit tunnels 2015-01-21 14:40:48 -05:00
orignal
e7f849184c some cleanup 2015-01-21 12:08:15 -05:00
orignal
c61cd350ee send multiple messages though single write call 2015-01-20 22:35:27 -05:00
orignal
ea353ac3ba send batch of I2NP messages 2015-01-20 21:05:57 -05:00
orignal
74c89ce06e proper cleanup of pending tunnels 2015-01-20 18:06:42 -05:00
orignal
42354ee5d5 removed useless mutex lock 2015-01-20 07:50:25 -05:00
orignal
e4d0fdaa56 Merge pull request #150 from chris-barry/patch-1
Update copyright
2015-01-19 22:29:45 -05:00
orignal
ebb5c53c3a use shared_ptr for TunnelPool 2015-01-19 22:28:13 -05:00
Chris Barry
676892cb00 Update copyright
Changing Debian information to match information in root directory.
2015-01-19 21:06:30 -05:00
orignal
e09da5cb54 correct CRC32 verification at big endian CPU 2015-01-19 14:30:30 -05:00
orignal
027c43c99c Reseed through I2PControl 2015-01-19 13:57:37 -05:00
orignal
f5b937667a don't try to re-request expired LeaseSet 2015-01-19 12:31:14 -05:00
orignal
f36229bd95 make sure DatabaseStore message is first 2015-01-18 18:46:22 -05:00
orignal
3c9e6054b5 use shared local destination for proxies 2015-01-17 09:42:44 -05:00
orignal
284fb5458e fixed resolve bug 2015-01-17 08:22:43 -05:00
orignal
bf7b53a2a6 resolve address for NTCP 2015-01-16 23:01:40 -05:00
orignal
07c6f2a20b make HTTP header if necessary 2015-01-16 16:51:52 -05:00
orignal
514947ba49 skip HTTP header 2015-01-16 16:19:17 -05:00
orignal
f3fbf6bd89 address resolver for NTCP address 2015-01-16 15:25:44 -05:00
Mikal Villa
0f227e8317 For some reason, the daemon src don't include I2PControl.cpp for OSX
builds.
2015-01-16 20:37:52 +01:00
Mikal Villa
905aa7cdc4 Adding download link 2015-01-16 02:44:18 +01:00
orignal
25cc118890 I2PControl method 2015-01-15 16:42:28 -05:00
orignal
72a4f8a9a1 fixed crash on shutdown 2015-01-14 20:27:19 -05:00
orignal
e898e6bf82 use RouterInfo request callback instead timeout 2015-01-14 16:37:03 -05:00
orignal
ad9d7931f5 RequestComplete for RouterInfo 2015-01-14 16:11:09 -05:00
orignal
fb3c577601 handle i2p.router.netdb.activepeers 2015-01-14 13:24:25 -05:00
orignal
02b7cd71c5 handle i2p.router.netdb.activepeers 2015-01-14 13:21:41 -05:00
orignal
98e930bd46 moved delayed queue to Peer 2015-01-13 22:19:13 -05:00
orignal
3481161616 send messages through Peer 2015-01-13 21:31:39 -05:00
orignal
a3352ac1dc Merge pull request #148 from Nefelim4ag/master
Makefile fixes
2015-01-13 11:26:05 -05:00
Timofey Titovets
214eb0caa5 Not overwrite -fPIC with CXXFLAGS 2015-01-13 15:14:52 +03:00
Timofey Titovets
3a30c00dae Fix: mkdir exist in multi thread building 2015-01-13 12:55:14 +03:00
orignal
d971dff593 introduced Peer 2015-01-12 22:53:35 -05:00
orignal
1eef996701 NetworkSetting method 2015-01-12 14:31:45 -05:00
orignal
dcae7fc541 participating request 2015-01-12 14:03:20 -05:00
orignal
aeb2e235e5 use lookup tables for requests 2015-01-12 13:38:16 -05:00
orignal
ff856d2f20 fixed race condition 2015-01-12 12:15:54 -05:00
Riccardo Spagni
a15c2c5d86 license -> BSD 3-clause 2015-01-12 17:54:09 +02:00
orignal
c37d13af84 Merge pull request #147 from space-and-time/master
Fixed windows build, assertion failed
2015-01-12 07:29:03 -05:00
root
bf443265ff fixed boost\asio\detail\socket_types.hpp(24) fatal error C1189: "WinSock.h has already been included" 2015-01-12 08:53:24 +04:00
root
9ebe38e59d added I2PService to VS project 2015-01-12 08:50:22 +04:00
orignal
a85cc6aa77 fixed race condition 2015-01-11 21:00:38 -05:00
orignal
6683a9cf76 moved NTCP to separate thread 2015-01-11 17:41:56 -05:00
orignal
e3e0702813 fxied crash at startup 2015-01-11 17:40:11 -05:00
orignal
d8942a3359 use TransportSession for sending messages 2015-01-10 23:00:27 -05:00
orignal
4d25634b66 less agressive exploratory 2015-01-10 16:08:13 -05:00
orignal
717940d969 some cleanup 2015-01-10 09:07:07 -05:00
orignal
912146b1c9 shutdown and graceful shutdown through I2PControl 2015-01-09 22:27:52 -05:00
orignal
70b6c024bf handle i2p.router.netdb.knownpeers RouterInfo request 2015-01-09 11:58:14 -05:00
orignal
047f08b482 put dot-separated params 2015-01-09 11:12:22 -05:00
orignal
4ce3817d28 put dot-separated params 2015-01-09 11:11:35 -05:00
orignal
8910412068 pass results by reference 2015-01-09 10:28:16 -05:00
orignal
c61ed150b7 check for pending LeaseSet request 2015-01-08 22:04:41 -05:00
orignal
1bbb86d304 changed addressbook subscription update interval to 12 hours 2015-01-08 16:16:56 -05:00
orignal
2a76f1decd publish own RouterInfo every 40 minutes if nothing changed 2015-01-08 16:14:05 -05:00
orignal
f3548daede I2PControl Authenticate and Echo 2015-01-08 16:11:40 -05:00
orignal
efdadfd7c5 added I2PControl to ClientContext 2015-01-08 13:28:51 -05:00
orignal
e82507ca4e call TCPIPAccetor::Start from I2PClientTunnel::Start 2015-01-08 07:39:35 -05:00
orignal
d67db32015 Merge pull request #146 from klondi/i2pservice
I2pservice
2015-01-07 22:52:21 -05:00
Francisco Blas (klondike) Izquierdo Riera
56014962d4 Make I2PClientTunnel use TCPIPAcceptor 2015-01-08 03:49:35 +01:00
Francisco Blas (klondike) Izquierdo Riera
df3e8ce937 Move Stream creation to its own handler for cleanliness, it will hand over to a tunnel connection when done 2015-01-08 03:28:54 +01:00
Francisco Blas (klondike) Izquierdo Riera
7d9c0b76fc Make SOCKS use TCPIPAcceptor 2015-01-08 01:45:49 +01:00
Francisco Blas (klondike) Izquierdo Riera
8a6bea64bc Make the HTTP Proxy use TCPIPAcceptor 2015-01-08 01:35:42 +01:00
Francisco Blas (klondike) Izquierdo Riera
114022d18a Add the TCPIPAcceptor class for handling TCP/IP services on clearnet 2015-01-08 01:34:31 +01:00
orignal
a85af553b8 Merge branch 'master' of https://github.com/PrivacySolutions/i2pd 2015-01-07 16:51:09 -05:00
orignal
1cf5a0c948 Merge pull request #145 from klondi/i2pservice
Solve weak_ptr issue
2015-01-07 16:50:22 -05:00
Francisco Blas (klondike) Izquierdo Riera
6643b4188a Solve weak_ptr issue 2015-01-07 22:49:28 +01:00
orignal
bc11689f35 extract params 2015-01-07 16:41:11 -05:00
orignal
0339a4f963 JSON parser 2015-01-07 16:09:32 -05:00
orignal
6a39f48a9e Merge pull request #144 from klondi/i2pservice
I2pservice
2015-01-07 16:01:56 -05:00
Francisco Blas (klondike) Izquierdo Riera
b3232b42db Use shared_from_this to avoid being killed easily on stop 2015-01-07 21:58:58 +01:00
Francisco Blas (klondike) Izquierdo Riera
c1a29b08ac Remove ClientContext.h dependency 2015-01-07 21:58:58 +01:00
orignal
75f6cc4319 Merge pull request #143 from klondi/i2pservice
I2pservice
2015-01-07 15:20:18 -05:00
Francisco Blas (klondike) Izquierdo Riera
bcbe207515 Make HTTPProxy use SIGNING_KEY_TYPE_DSA_SHA1 2015-01-07 21:15:04 +01:00
Francisco Blas (klondike) Izquierdo Riera
29039fd039 Merge with upstream 2015-01-07 20:47:47 +01:00
Francisco Blas (klondike) Izquierdo Riera
90005c8237 Migrate to I2Pservice 2015-01-07 20:44:24 +01:00
orignal
cdc0aa658a I2PControl added 2015-01-07 13:26:44 -05:00
Francisco Blas (klondike) Izquierdo Riera
dd42819a2f Create I2Pservice as a way to integrate service management, hide unnecessary handlers 2015-01-07 19:09:59 +01:00
orignal
88560d06a1 Merge pull request #141 from klondi/httpproxy
Force Connection: Close and allow http/1.1
2015-01-06 20:42:41 -05:00
Francisco Blas (klondike) Izquierdo Riera
cb8a465605 Force Connection: Close and allow http/1.1 2015-01-07 02:40:57 +01:00
orignal
fe13a85c0f read all available data from closed stream 2015-01-06 19:05:48 -05:00
orignal
89b6be91a3 Merge pull request #140 from klondi/httpproxy
Httpproxy
2015-01-06 19:00:41 -05:00
Francisco Blas (klondike) Izquierdo Riera
6aca908462 Initial HTTPProxy support by simply transferring control to a tunnel 2015-01-07 00:15:54 +01:00
Francisco Blas (klondike) Izquierdo Riera
634718d6b4 Detect null stream on I2PConnect 2015-01-07 00:15:54 +01:00
Francisco Blas (klondike) Izquierdo Riera
2fca028161 Remove unnecessary header 2015-01-07 00:15:54 +01:00
Francisco Blas (klondike) Izquierdo Riera
5e8d28abba Reorder SOCKS headers for cleanness 2015-01-07 00:15:54 +01:00
Francisco Blas (klondike) Izquierdo Riera
bc78460f63 Enter state BEFORE reading data to avoid race conditions 2015-01-07 00:15:54 +01:00
orignal
e228ba963d Merge pull request #139 from iShift/patch-1
fix deps for ubuntu/debian
2015-01-06 16:00:47 -05:00
iShift
63927fc1fa fix deps for ubuntu/debian
fix https://github.com/PrivacySolutions/i2pd/issues/137
2015-01-06 23:52:13 +03:00
orignal
cbcfe50eb5 Merge pull request #138 from klondi/master
Reintroduce Request verification, allow for extra data after connect request.
2015-01-06 12:51:25 -05:00
Francisco Blas (klondike) Izquierdo Riera
7da95bd28a Introduce missing call to request verification, so unknown address types and commands are handled properly, allow for extra socket data after the request for fast request sending, it will just be forwarded on I2PConnect 2015-01-06 18:49:00 +01:00
85 changed files with 4630 additions and 2265 deletions

View File

@@ -355,10 +355,13 @@ namespace client
void AddressBook::DownloadComplete (bool success)
{
m_IsDownloading = false;
m_SubscriptionsUpdateTimer->expires_from_now (boost::posix_time::minutes(
success ? CONTINIOUS_SUBSCRIPTION_UPDATE_TIMEOUT : CONTINIOUS_SUBSCRIPTION_RETRY_TIMEOUT));
m_SubscriptionsUpdateTimer->async_wait (std::bind (&AddressBook::HandleSubscriptionsUpdateTimer,
this, std::placeholders::_1));
if (m_SubscriptionsUpdateTimer)
{
m_SubscriptionsUpdateTimer->expires_from_now (boost::posix_time::minutes(
success ? CONTINIOUS_SUBSCRIPTION_UPDATE_TIMEOUT : CONTINIOUS_SUBSCRIPTION_RETRY_TIMEOUT));
m_SubscriptionsUpdateTimer->async_wait (std::bind (&AddressBook::HandleSubscriptionsUpdateTimer,
this, std::placeholders::_1));
}
}
void AddressBook::StartSubscriptions ()
@@ -432,7 +435,7 @@ namespace client
{
std::condition_variable newDataReceived;
std::mutex newDataReceivedMutex;
const i2p::data::LeaseSet * leaseSet = i2p::data::netdb.FindLeaseSet (ident);
auto leaseSet = i2p::client::context.GetSharedLocalDestination ()->FindLeaseSet (ident);
if (!leaseSet)
{
bool found = false;
@@ -459,7 +462,7 @@ namespace client
if (m_LastModified.length () > 0) // if-modfief-since
request << i2p::util::http::IF_MODIFIED_SINCE << ": " << m_LastModified << "\r\n";
request << "\r\n"; // end of header
auto stream = i2p::client::context.GetSharedLocalDestination ()->CreateStream (*leaseSet, u.port_);
auto stream = i2p::client::context.GetSharedLocalDestination ()->CreateStream (leaseSet, u.port_);
stream->Send ((uint8_t *)request.str ().c_str (), request.str ().length ());
uint8_t buf[4095];

View File

@@ -20,9 +20,11 @@ namespace client
const char DEFAULT_SUBSCRIPTION_ADDRESS[] = "http://udhdrtrcetjm5sxzskjyr5ztpeszydbh4dpl3pl4utgqqw2v4jna.b32.i2p/hosts.txt";
const int INITIAL_SUBSCRIPTION_UPDATE_TIMEOUT = 3; // in minutes
const int INITIAL_SUBSCRIPTION_RETRY_TIMEOUT = 1; // in minutes
const int CONTINIOUS_SUBSCRIPTION_UPDATE_TIMEOUT = 240; // in minutes
const int CONTINIOUS_SUBSCRIPTION_UPDATE_TIMEOUT = 720; // in minutes (12 hours)
const int CONTINIOUS_SUBSCRIPTION_RETRY_TIMEOUT = 5; // in minutes
const int SUBSCRIPTION_REQUEST_TIMEOUT = 60; //in second
inline std::string GetB32Address(const i2p::data::IdentHash& ident) { return ident.ToBase32().append(".b32.i2p"); }
class AddressBookStorage // interface for storage
{
@@ -55,7 +57,7 @@ namespace client
void LoadHostsFromStream (std::istream& f);
void DownloadComplete (bool success);
//This method returns the ".b32.i2p" address
std::string ToAddress(const i2p::data::IdentHash& ident) { return ident.ToBase32().append(".b32.i2p"); }
std::string ToAddress(const i2p::data::IdentHash& ident) { return GetB32Address(ident); }
std::string ToAddress(const i2p::data::IdentityEx& ident) { return ToAddress(ident.GetIdentHash ()); }
private:

10
BOB.cpp
View File

@@ -28,7 +28,7 @@ namespace client
void BOBI2PInboundTunnel::Stop ()
{
m_Acceptor.close();
ClearConnections ();
ClearHandlers ();
}
void BOBI2PInboundTunnel::Accept ()
@@ -132,11 +132,11 @@ namespace client
delete receiver;
}
void BOBI2PInboundTunnel::CreateConnection (AddressReceiver * receiver, const i2p::data::LeaseSet * leaseSet)
void BOBI2PInboundTunnel::CreateConnection (AddressReceiver * receiver, std::shared_ptr<const i2p::data::LeaseSet> leaseSet)
{
LogPrint ("New BOB inbound connection");
auto connection = std::make_shared<I2PTunnelConnection>(this, receiver->socket, leaseSet);
AddConnection (connection);
AddHandler (connection);
connection->I2PConnect (receiver->data, receiver->dataLen);
delete receiver;
}
@@ -154,7 +154,7 @@ namespace client
void BOBI2POutboundTunnel::Stop ()
{
ClearConnections ();
ClearHandlers ();
}
void BOBI2POutboundTunnel::Accept ()
@@ -171,7 +171,7 @@ namespace client
if (stream)
{
auto conn = std::make_shared<I2PTunnelConnection> (this, stream, new boost::asio::ip::tcp::socket (GetService ()), m_Endpoint, m_IsQuiet);
AddConnection (conn);
AddHandler (conn);
conn->Connect ();
}
}

7
BOB.h
View File

@@ -8,6 +8,7 @@
#include <string>
#include <boost/asio.hpp>
#include "I2PTunnel.h"
#include "I2PService.h"
#include "Identity.h"
#include "LeaseSet.h"
@@ -41,12 +42,12 @@ namespace client
const char BOB_REPLY_ERROR[] = "ERROR %s\n";
const char BOB_DATA[] = "NICKNAME %s\n";
class BOBI2PTunnel: public I2PTunnel
class BOBI2PTunnel: public I2PService
{
public:
BOBI2PTunnel (ClientDestination * localDestination):
I2PTunnel (localDestination) {};
I2PService (localDestination) {};
virtual void Start () {};
virtual void Stop () {};
@@ -83,7 +84,7 @@ namespace client
void HandleDestinationRequestTimer (const boost::system::error_code& ecode, AddressReceiver * receiver, i2p::data::IdentHash ident);
void CreateConnection (AddressReceiver * receiver, const i2p::data::LeaseSet * leaseSet);
void CreateConnection (AddressReceiver * receiver, std::shared_ptr<const i2p::data::LeaseSet> leaseSet);
private:

View File

@@ -1,4 +1,7 @@
#include <fstream>
#include <iostream>
#include <boost/program_options/parsers.hpp>
#include <boost/program_options/variables_map.hpp>
#include "util.h"
#include "Log.h"
#include "Identity.h"
@@ -11,8 +14,8 @@ namespace client
ClientContext context;
ClientContext::ClientContext (): m_SharedLocalDestination (nullptr),
m_HttpProxy (nullptr), m_SocksProxy (nullptr), m_IrcTunnel (nullptr),
m_ServerTunnel (nullptr), m_SamBridge (nullptr), m_BOBCommandChannel (nullptr)
m_HttpProxy (nullptr), m_SocksProxy (nullptr), m_SamBridge (nullptr),
m_BOBCommandChannel (nullptr), m_I2PControlService (nullptr)
{
}
@@ -20,10 +23,9 @@ namespace client
{
delete m_HttpProxy;
delete m_SocksProxy;
delete m_IrcTunnel;
delete m_ServerTunnel;
delete m_SamBridge;
delete m_BOBCommandChannel;
delete m_I2PControlService;
}
void ClientContext::Start ()
@@ -35,12 +37,15 @@ namespace client
m_SharedLocalDestination->Start ();
}
// proxies
m_HttpProxy = new i2p::proxy::HTTPProxy(i2p::util::config::GetArg("-httpproxyport", 4446));
m_HttpProxy->Start();
LogPrint("HTTP Proxy started");
m_SocksProxy = new i2p::proxy::SOCKSProxy(i2p::util::config::GetArg("-socksproxyport", 4447));
m_SocksProxy->Start();
LogPrint("SOCKS Proxy Started");
// I2P tunnels
std::string ircDestination = i2p::util::config::GetArg("-ircdest", "");
if (ircDestination.length () > 0) // ircdest is presented
{
@@ -48,19 +53,25 @@ namespace client
std::string ircKeys = i2p::util::config::GetArg("-irckeys", "");
if (ircKeys.length () > 0)
localDestination = LoadLocalDestination (ircKeys, false);
m_IrcTunnel = new I2PClientTunnel (ircDestination, i2p::util::config::GetArg("-ircport", 6668), localDestination);
m_IrcTunnel->Start ();
auto ircPort = i2p::util::config::GetArg("-ircport", 6668);
auto ircTunnel = new I2PClientTunnel (ircDestination, ircPort, localDestination);
ircTunnel->Start ();
m_ClientTunnels.insert (std::make_pair(ircPort, std::unique_ptr<I2PClientTunnel>(ircTunnel)));
LogPrint("IRC tunnel started");
}
std::string eepKeys = i2p::util::config::GetArg("-eepkeys", "");
if (eepKeys.length () > 0) // eepkeys file is presented
{
auto localDestination = LoadLocalDestination (eepKeys, true);
m_ServerTunnel = new I2PServerTunnel (i2p::util::config::GetArg("-eephost", "127.0.0.1"),
auto serverTunnel = new I2PServerTunnel (i2p::util::config::GetArg("-eephost", "127.0.0.1"),
i2p::util::config::GetArg("-eepport", 80), localDestination);
m_ServerTunnel->Start ();
serverTunnel->Start ();
m_ServerTunnels.insert (std::make_pair(localDestination->GetIdentHash (), std::unique_ptr<I2PServerTunnel>(serverTunnel)));
LogPrint("Server tunnel started");
}
ReadTunnels ();
// SAM
int samPort = i2p::util::config::GetArg("-samport", 0);
if (samPort)
{
@@ -68,6 +79,8 @@ namespace client
m_SamBridge->Start ();
LogPrint("SAM bridge started");
}
// BOB
int bobPort = i2p::util::config::GetArg("-bobport", 0);
if (bobPort)
{
@@ -75,6 +88,15 @@ namespace client
m_BOBCommandChannel->Start ();
LogPrint("BOB command channel started");
}
// I2P Control
int i2pcontrolPort = i2p::util::config::GetArg("-i2pcontrolport", 0);
if (i2pcontrolPort)
{
m_I2PControlService = new I2PControlService (i2pcontrolPort);
m_I2PControlService->Start ();
LogPrint("I2PControl started");
}
m_AddressBook.StartSubscriptions ();
}
@@ -84,39 +106,44 @@ namespace client
m_HttpProxy->Stop();
delete m_HttpProxy;
m_HttpProxy = nullptr;
LogPrint("HTTP Proxy stoped");
LogPrint("HTTP Proxy stopped");
m_SocksProxy->Stop();
delete m_SocksProxy;
m_SocksProxy = nullptr;
LogPrint("SOCKS Proxy stoped");
if (m_IrcTunnel)
LogPrint("SOCKS Proxy stopped");
for (auto& it: m_ClientTunnels)
{
m_IrcTunnel->Stop ();
delete m_IrcTunnel;
m_IrcTunnel = nullptr;
LogPrint("IRC tunnel stoped");
it.second->Stop ();
LogPrint("I2P client tunnel on port ", it.first, " stopped");
}
if (m_ServerTunnel)
m_ClientTunnels.clear ();
for (auto& it: m_ServerTunnels)
{
m_ServerTunnel->Stop ();
delete m_ServerTunnel;
m_ServerTunnel = nullptr;
LogPrint("Server tunnel stoped");
}
it.second->Stop ();
LogPrint("I2P server tunnel stopped");
}
m_ServerTunnels.clear ();
if (m_SamBridge)
{
m_SamBridge->Stop ();
delete m_SamBridge;
m_SamBridge = nullptr;
LogPrint("SAM brdige stoped");
LogPrint("SAM brdige stopped");
}
if (m_BOBCommandChannel)
{
m_BOBCommandChannel->Stop ();
delete m_BOBCommandChannel;
m_BOBCommandChannel = nullptr;
LogPrint("BOB command channel stoped");
LogPrint("BOB command channel stopped");
}
if (m_I2PControlService)
{
m_I2PControlService->Stop ();
delete m_I2PControlService;
m_I2PControlService = nullptr;
LogPrint("I2PControl stopped");
}
for (auto it: m_Destinations)
{
@@ -157,10 +184,20 @@ namespace client
LogPrint ("New private keys file ", fullPath, " for ", m_AddressBook.ToAddress(keys.GetPublic ().GetIdentHash ()), " created");
}
auto localDestination = new ClientDestination (keys, isPublic);
ClientDestination * localDestination = nullptr;
std::unique_lock<std::mutex> l(m_DestinationsMutex);
m_Destinations[localDestination->GetIdentHash ()] = localDestination;
localDestination->Start ();
auto it = m_Destinations.find (keys.GetPublic ().GetIdentHash ());
if (it != m_Destinations.end ())
{
LogPrint (eLogWarning, "Local destination ", m_AddressBook.ToAddress(keys.GetPublic ().GetIdentHash ()), " alreday exists");
localDestination = it->second;
}
else
{
localDestination = new ClientDestination (keys, isPublic);
m_Destinations[localDestination->GetIdentHash ()] = localDestination;
localDestination->Start ();
}
return localDestination;
}
@@ -219,5 +256,80 @@ namespace client
return it->second;
return nullptr;
}
void ClientContext::ReadTunnels ()
{
std::ifstream ifs (i2p::util::filesystem::GetFullPath (TUNNELS_CONFIG_FILENAME));
if (ifs.good ())
{
boost::program_options::options_description params ("I2P tunnels parameters");
params.add_options ()
// client
(I2P_CLIENT_TUNNEL_NAME, boost::program_options::value<std::vector<std::string> >(), "tunnel name")
(I2P_CLIENT_TUNNEL_PORT, boost::program_options::value<std::vector<int> >(), "Local port")
(I2P_CLIENT_TUNNEL_DESTINATION, boost::program_options::value<std::vector<std::string> >(), "destination")
(I2P_CLIENT_TUNNEL_KEYS, boost::program_options::value<std::vector<std::string> >(), "keys")
// server
(I2P_SERVER_TUNNEL_NAME, boost::program_options::value<std::vector<std::string> >(), "tunnel name")
(I2P_SERVER_TUNNEL_HOST, boost::program_options::value<std::vector<std::string> >(), "host")
(I2P_SERVER_TUNNEL_PORT, boost::program_options::value<std::vector<int> >(), "port")
(I2P_SERVER_TUNNEL_KEYS, boost::program_options::value<std::vector<std::string> >(), "keys")
;
boost::program_options::variables_map vm;
try
{
boost::program_options::store (boost::program_options::parse_config_file (ifs, params), vm);
boost::program_options::notify (vm);
}
catch (boost::program_options::error& ex)
{
LogPrint (eLogError, "Can't parse ", TUNNELS_CONFIG_FILENAME,": ", ex.what ());
return;
}
if (vm.count (I2P_CLIENT_TUNNEL_NAME) > 0)
{
auto names = vm[I2P_CLIENT_TUNNEL_NAME].as<std::vector<std::string> >();
int numClientTunnels = names.size ();
auto ports = vm[I2P_CLIENT_TUNNEL_PORT].as<std::vector<int> >();
auto destinations = vm[I2P_CLIENT_TUNNEL_DESTINATION].as<std::vector<std::string> >();
auto keys = vm[I2P_CLIENT_TUNNEL_KEYS].as<std::vector<std::string> >();
for (int i = 0; i < numClientTunnels; i++)
{
ClientDestination * localDestination = nullptr;
if (keys[i].length () > 0)
localDestination = LoadLocalDestination (keys[i], false);
auto clientTunnel = new I2PClientTunnel (destinations[i], ports[i], localDestination);
if (m_ClientTunnels.insert (std::make_pair (ports[i], std::unique_ptr<I2PClientTunnel>(clientTunnel))).second)
clientTunnel->Start ();
else
LogPrint (eLogError, "I2P client tunnel with port ", ports[i], " already exists");
}
LogPrint (eLogInfo, numClientTunnels, " I2P client tunnels created");
}
if (vm.count (I2P_SERVER_TUNNEL_NAME) > 0)
{
auto names = vm[I2P_SERVER_TUNNEL_NAME].as<std::vector<std::string> >();
int numServerTunnels = names.size ();
auto hosts = vm[I2P_SERVER_TUNNEL_HOST].as<std::vector<std::string> >();
auto ports = vm[I2P_SERVER_TUNNEL_PORT].as<std::vector<int> >();
auto keys = vm[I2P_SERVER_TUNNEL_KEYS].as<std::vector<std::string> >();
for (int i = 0; i < numServerTunnels; i++)
{
auto localDestination = LoadLocalDestination (keys[i], true);
auto serverTunnel = new I2PServerTunnel (hosts[i], ports[i], localDestination);
if (m_ServerTunnels.insert (std::make_pair (localDestination->GetIdentHash (), std::unique_ptr<I2PServerTunnel>(serverTunnel))).second)
serverTunnel->Start ();
else
LogPrint (eLogError, "I2P server tunnel for destination ", m_AddressBook.ToAddress(localDestination->GetIdentHash ()), " already exists");
}
LogPrint (eLogInfo, numServerTunnels, " I2P server tunnels created");
}
}
}
}
}

View File

@@ -1,7 +1,9 @@
#ifndef CLIENT_CONTEXT_H__
#define CLIENT_CONTEXT_H__
#include <map>
#include <mutex>
#include <memory>
#include "Destination.h"
#include "HTTPProxy.h"
#include "SOCKS.h"
@@ -9,11 +11,22 @@
#include "SAM.h"
#include "BOB.h"
#include "AddressBook.h"
#include "I2PControl.h"
namespace i2p
{
namespace client
{
const char I2P_CLIENT_TUNNEL_NAME[] = "client.name";
const char I2P_CLIENT_TUNNEL_PORT[] = "client.port";
const char I2P_CLIENT_TUNNEL_DESTINATION[] = "client.destination";
const char I2P_CLIENT_TUNNEL_KEYS[] = "client.keys";
const char I2P_SERVER_TUNNEL_NAME[] = "server.name";
const char I2P_SERVER_TUNNEL_HOST[] = "server.host";
const char I2P_SERVER_TUNNEL_PORT[] = "server.port";
const char I2P_SERVER_TUNNEL_KEYS[] = "server.keys";
const char TUNNELS_CONFIG_FILENAME[] = "tunnels.cfg";
class ClientContext
{
public:
@@ -34,7 +47,12 @@ namespace client
ClientDestination * LoadLocalDestination (const std::string& filename, bool isPublic);
AddressBook& GetAddressBook () { return m_AddressBook; };
const SAMBridge * GetSAMBridge () const { return m_SamBridge; };
private:
void ReadTunnels ();
private:
std::mutex m_DestinationsMutex;
@@ -45,10 +63,11 @@ namespace client
i2p::proxy::HTTPProxy * m_HttpProxy;
i2p::proxy::SOCKSProxy * m_SocksProxy;
I2PClientTunnel * m_IrcTunnel;
I2PServerTunnel * m_ServerTunnel;
std::map<int, std::unique_ptr<I2PClientTunnel> > m_ClientTunnels; // port->tunnel
std::map<i2p::data::IdentHash, std::unique_ptr<I2PServerTunnel> > m_ServerTunnels; // destination->tunnel
SAMBridge * m_SamBridge;
BOBCommandChannel * m_BOBCommandChannel;
I2PControlService * m_I2PControlService;
public:
// for HTTP

View File

@@ -78,6 +78,7 @@ namespace i2p
i2p::context.SetUnreachable ();
i2p::context.SetSupportsV6 (i2p::util::config::GetArg("-v6", 0));
i2p::context.SetFloodfill (i2p::util::config::GetArg("-floodfill", 0));
LogPrint("CMD parameters:");
for (int i = 0; i < argc; ++i)
@@ -127,15 +128,15 @@ namespace i2p
{
LogPrint("Shutdown started.");
i2p::client::context.Stop();
LogPrint("Client stoped");
LogPrint("Client stopped");
i2p::tunnel::tunnels.Stop();
LogPrint("Tunnels stoped");
LogPrint("Tunnels stopped");
i2p::transport::transports.Stop();
LogPrint("Transports stoped");
LogPrint("Transports stopped");
i2p::data::netdb.Stop();
LogPrint("NetDB stoped");
LogPrint("NetDB stopped");
d.httpServer->Stop();
LogPrint("HTTP Server stoped");
LogPrint("HTTP Server stopped");
#ifdef USE_UPNP
i2p::UPnP::upnpc.Stop();
#endif

View File

@@ -17,7 +17,7 @@ namespace datagram
{
}
void DatagramDestination::SendDatagramTo (const uint8_t * payload, size_t len, const i2p::data::LeaseSet& remote)
void DatagramDestination::SendDatagramTo (const uint8_t * payload, size_t len, std::shared_ptr<const i2p::data::LeaseSet> remote)
{
uint8_t buf[MAX_DATAGRAM_SIZE];
auto identityLen = m_Owner.GetIdentity ().ToBuffer (buf, MAX_DATAGRAM_SIZE);
@@ -40,10 +40,10 @@ namespace datagram
CreateDataMessage (buf, len + headerLen), remote));
}
void DatagramDestination::SendMsg (I2NPMessage * msg, const i2p::data::LeaseSet& remote)
void DatagramDestination::SendMsg (I2NPMessage * msg, std::shared_ptr<const i2p::data::LeaseSet> remote)
{
auto outboundTunnel = m_Owner.GetTunnelPool ()->GetNextOutboundTunnel ();
auto leases = remote.GetNonExpiredLeases ();
auto leases = remote->GetNonExpiredLeases ();
if (!leases.empty () && outboundTunnel)
{
std::vector<i2p::tunnel::TunnelMessageBlock> msgs;

View File

@@ -2,6 +2,7 @@
#define DATAGRAM_H__
#include <inttypes.h>
#include <memory>
#include <functional>
#include "Identity.h"
#include "LeaseSet.h"
@@ -25,7 +26,7 @@ namespace datagram
DatagramDestination (i2p::client::ClientDestination& owner);
~DatagramDestination () {};
void SendDatagramTo (const uint8_t * payload, size_t len, const i2p::data::LeaseSet& remote);
void SendDatagramTo (const uint8_t * payload, size_t len, std::shared_ptr<const i2p::data::LeaseSet> remote);
void HandleDataMessagePayload (const uint8_t * buf, size_t len);
void SetReceiver (const Receiver& receiver) { m_Receiver = receiver; };
@@ -34,7 +35,7 @@ namespace datagram
private:
I2NPMessage * CreateDataMessage (const uint8_t * payload, size_t len);
void SendMsg (I2NPMessage * msg, const i2p::data::LeaseSet& remote);
void SendMsg (I2NPMessage * msg, std::shared_ptr<const i2p::data::LeaseSet> remote);
void HandleDatagram (const uint8_t * buf, size_t len);
private:

View File

@@ -6,7 +6,7 @@
#include "ElGamal.h"
#include "Timestamp.h"
#include "NetDb.h"
#include "ClientContext.h"
#include "AddressBook.h"
#include "Destination.h"
namespace i2p
@@ -17,7 +17,7 @@ namespace client
const std::map<std::string, std::string> * params):
m_IsRunning (false), m_Thread (nullptr), m_Work (m_Service),
m_Keys (keys), m_LeaseSet (nullptr), m_IsPublic (isPublic), m_PublishReplyToken (0),
m_DatagramDestination (nullptr), m_PublishConfirmationTimer (m_Service)
m_DatagramDestination (nullptr), m_PublishConfirmationTimer (m_Service), m_CleanupTimer (m_Service)
{
i2p::crypto::GenerateElGamalKeyPair(i2p::context.GetRandomNumberGenerator (), m_EncryptionPrivateKey, m_EncryptionPublicKey);
int inboundTunnelLen = DEFAULT_INBOUND_TUNNEL_LENGTH;
@@ -47,7 +47,7 @@ namespace client
}
m_Pool = i2p::tunnel::tunnels.CreateTunnelPool (this, inboundTunnelLen, outboundTunnelLen);
if (m_IsPublic)
LogPrint (eLogInfo, "Local address ", i2p::client::context.GetAddressBook ().ToAddress(GetIdentHash()), " created");
LogPrint (eLogInfo, "Local address ", i2p::client::GetB32Address(GetIdentHash()), " created");
m_StreamingDestination = new i2p::stream::StreamingDestination (*this); // TODO:
}
@@ -57,8 +57,6 @@ namespace client
Stop ();
for (auto it: m_LeaseSetRequests)
delete it.second;
for (auto it: m_RemoteLeaseSets)
delete it.second;
if (m_Pool)
i2p::tunnel::tunnels.DeleteTunnelPool (m_Pool);
if (m_StreamingDestination)
@@ -91,6 +89,10 @@ namespace client
m_Pool->SetActive (true);
m_Thread = new std::thread (std::bind (&ClientDestination::Run, this));
m_StreamingDestination->Start ();
m_CleanupTimer.expires_from_now (boost::posix_time::minutes (DESTINATION_CLEANUP_TIMEOUT));
m_CleanupTimer.async_wait (std::bind (&ClientDestination::HandleCleanupTimer,
this, std::placeholders::_1));
}
}
@@ -98,6 +100,7 @@ namespace client
{
if (m_IsRunning)
{
m_CleanupTimer.cancel ();
m_IsRunning = false;
m_StreamingDestination->Stop ();
if (m_DatagramDestination)
@@ -121,7 +124,7 @@ namespace client
}
}
const i2p::data::LeaseSet * ClientDestination::FindLeaseSet (const i2p::data::IdentHash& ident)
std::shared_ptr<const i2p::data::LeaseSet> ClientDestination::FindLeaseSet (const i2p::data::IdentHash& ident)
{
auto it = m_RemoteLeaseSets.find (ident);
if (it != m_RemoteLeaseSets.end ())
@@ -129,17 +132,13 @@ namespace client
if (it->second->HasNonExpiredLeases ())
return it->second;
else
{
LogPrint ("All leases of remote LeaseSet expired. Request it");
RequestDestination (ident);
}
LogPrint ("All leases of remote LeaseSet expired");
}
else
{
auto ls = i2p::data::netdb.FindLeaseSet (ident);
if (ls)
{
ls = new i2p::data::LeaseSet (*ls);
m_RemoteLeaseSets[ident] = ls;
return ls;
}
@@ -193,7 +192,7 @@ namespace client
m_Service.post (std::bind (&ClientDestination::HandleDeliveryStatusMessage, this, msg));
}
void ClientDestination::HandleI2NPMessage (const uint8_t * buf, size_t len, i2p::tunnel::InboundTunnel * from)
void ClientDestination::HandleI2NPMessage (const uint8_t * buf, size_t len, std::shared_ptr<i2p::tunnel::InboundTunnel> from)
{
uint8_t typeID = buf[I2NP_HEADER_TYPEID_OFFSET];
switch (typeID)
@@ -216,8 +215,11 @@ namespace client
{
uint32_t replyToken = bufbe32toh (buf + DATABASE_STORE_REPLY_TOKEN_OFFSET);
size_t offset = DATABASE_STORE_HEADER_SIZE;
if (replyToken) // TODO:
if (replyToken)
{
LogPrint (eLogInfo, "Reply token is ignored for DatabaseStore");
offset += 36;
}
if (buf[DATABASE_STORE_TYPE_OFFSET] == 1) // LeaseSet
{
LogPrint (eLogDebug, "Remote LeaseSet");
@@ -230,7 +232,7 @@ namespace client
else
{
LogPrint (eLogDebug, "New remote LeaseSet added");
m_RemoteLeaseSets[buf + DATABASE_STORE_KEY_OFFSET] = new i2p::data::LeaseSet (buf + offset, len - offset);
m_RemoteLeaseSets[buf + DATABASE_STORE_KEY_OFFSET] = std::make_shared<i2p::data::LeaseSet> (buf + offset, len - offset);
}
}
else
@@ -341,7 +343,7 @@ namespace client
m_ExcludedFloodfills.insert (floodfill->GetIdentHash ());
LogPrint (eLogDebug, "Publish LeaseSet of ", GetIdentHash ().ToBase32 ());
m_PublishReplyToken = i2p::context.GetRandomNumberGenerator ().GenerateWord32 ();
auto msg = WrapMessage (*floodfill, i2p::CreateDatabaseStoreMsg (m_LeaseSet, m_PublishReplyToken));
auto msg = WrapMessage (floodfill, i2p::CreateDatabaseStoreMsg (m_LeaseSet, m_PublishReplyToken));
m_PublishConfirmationTimer.expires_from_now (boost::posix_time::seconds(PUBLISH_CONFIRMATION_TIMEOUT));
m_PublishConfirmationTimer.async_wait (std::bind (&ClientDestination::HandlePublishConfirmationTimer,
this, std::placeholders::_1));
@@ -387,23 +389,11 @@ namespace client
}
}
void ClientDestination::CreateStream (StreamRequestComplete streamRequestComplete, const std::string& dest, int port) {
assert(streamRequestComplete);
i2p::data::IdentHash identHash;
if (i2p::client::context.GetAddressBook ().GetIdentHash (dest, identHash))
CreateStream (streamRequestComplete, identHash, port);
else
{
LogPrint (eLogWarning, "Remote destination ", dest, " not found");
streamRequestComplete (nullptr);
}
}
void ClientDestination::CreateStream (StreamRequestComplete streamRequestComplete, const i2p::data::IdentHash& dest, int port) {
assert(streamRequestComplete);
const i2p::data::LeaseSet * leaseSet = FindLeaseSet (dest);
auto leaseSet = FindLeaseSet (dest);
if (leaseSet)
streamRequestComplete(CreateStream (*leaseSet, port));
streamRequestComplete(CreateStream (leaseSet, port));
else
{
RequestDestination (dest,
@@ -413,9 +403,9 @@ namespace client
streamRequestComplete (nullptr);
else
{
const i2p::data::LeaseSet * leaseSet = FindLeaseSet (dest);
auto leaseSet = FindLeaseSet (dest);
if (leaseSet)
streamRequestComplete(CreateStream (*leaseSet, port));
streamRequestComplete(CreateStream (leaseSet, port));
else
streamRequestComplete (nullptr);
}
@@ -423,7 +413,7 @@ namespace client
}
}
std::shared_ptr<i2p::stream::Stream> ClientDestination::CreateStream (const i2p::data::LeaseSet& remote, int port)
std::shared_ptr<i2p::stream::Stream> ClientDestination::CreateStream (std::shared_ptr<const i2p::data::LeaseSet> remote, int port)
{
if (m_StreamingDestination)
return m_StreamingDestination->CreateNewOutgoingStream (remote, port);
@@ -476,14 +466,24 @@ namespace client
{
LeaseSetRequest * request = new LeaseSetRequest (m_Service);
request->requestComplete = requestComplete;
m_LeaseSetRequests[dest] = request;
if (!SendLeaseSetRequest (dest, floodfill, request))
auto ret = m_LeaseSetRequests.insert (std::pair<i2p::data::IdentHash, LeaseSetRequest *>(dest,request));
if (ret.second) // inserted
{
// request failed
if (!SendLeaseSetRequest (dest, floodfill, request))
{
// request failed
if (request->requestComplete) request->requestComplete (false);
delete request;
m_LeaseSetRequests.erase (dest);
}
}
else // duplicate
{
LogPrint (eLogError, "Request of ", dest.ToBase64 (), " is pending already");
// TODO: queue up requests
if (request->requestComplete) request->requestComplete (false);
delete request;
m_LeaseSetRequests.erase (dest);
}
}
}
else
LogPrint (eLogError, "No floodfills found");
@@ -510,9 +510,9 @@ namespace client
rnd.GenerateBlock (replyTag, 32); // random session tag
AddSessionKey (replyKey, replyTag);
I2NPMessage * msg = WrapMessage (*nextFloodfill,
I2NPMessage * msg = WrapMessage (nextFloodfill,
CreateLeaseSetDatabaseLookupMsg (dest, request->excluded,
replyTunnel, replyKey, replyTag));
replyTunnel.get (), replyKey, replyTag));
outboundTunnel->SendTunnelDataMsg (
{
i2p::tunnel::TunnelMessageBlock
@@ -543,7 +543,7 @@ namespace client
{
auto floodfill = i2p::data::netdb.GetClosestFloodfill (dest, it->second->excluded);
if (floodfill)
SendLeaseSetRequest (dest, floodfill, it->second);
done = !SendLeaseSetRequest (dest, floodfill, it->second);
else
done = true;
}
@@ -561,6 +561,32 @@ namespace client
}
}
}
}
void ClientDestination::HandleCleanupTimer (const boost::system::error_code& ecode)
{
if (ecode != boost::asio::error::operation_aborted)
{
CleanupRoutingSessions ();
CleanupRemoteLeaseSets ();
m_CleanupTimer.expires_from_now (boost::posix_time::minutes (DESTINATION_CLEANUP_TIMEOUT));
m_CleanupTimer.async_wait (std::bind (&ClientDestination::HandleCleanupTimer,
this, std::placeholders::_1));
}
}
void ClientDestination::CleanupRemoteLeaseSets ()
{
for (auto it = m_RemoteLeaseSets.begin (); it != m_RemoteLeaseSets.end ();)
{
if (!it->second->HasNonExpiredLeases ()) // all leases expired
{
LogPrint ("Remote LeaseSet ", it->second->GetIdentHash ().ToBase64 (), " expired");
it = m_RemoteLeaseSets.erase (it);
}
else
it++;
}
}
}
}

View File

@@ -29,6 +29,7 @@ namespace client
const int LEASESET_REQUEST_TIMEOUT = 5; // in seconds
const int MAX_LEASESET_REQUEST_TIMEOUT = 40; // in seconds
const int MAX_NUM_FLOODFILLS_PER_REQUEST = 7;
const int DESTINATION_CLEANUP_TIMEOUT = 20; // in minutes
// I2CP
const char I2CP_PARAM_INBOUND_TUNNEL_LENGTH[] = "inbound.length";
@@ -36,7 +37,9 @@ namespace client
const char I2CP_PARAM_OUTBOUND_TUNNEL_LENGTH[] = "outbound.length";
const int DEFAULT_OUTBOUND_TUNNEL_LENGTH = 3;
const int STREAM_REQUEST_TIMEOUT = 60; //in seconds
typedef std::function<void (std::shared_ptr<i2p::stream::Stream> stream)> StreamRequestComplete;
class ClientDestination: public i2p::garlic::GarlicDestination
{
typedef std::function<void (bool success)> RequestComplete;
@@ -49,7 +52,6 @@ namespace client
RequestComplete requestComplete;
};
typedef std::function<void (std::shared_ptr<i2p::stream::Stream> stream)> StreamRequestComplete;
public:
@@ -60,16 +62,15 @@ namespace client
virtual void Stop ();
bool IsRunning () const { return m_IsRunning; };
boost::asio::io_service& GetService () { return m_Service; };
i2p::tunnel::TunnelPool * GetTunnelPool () { return m_Pool; };
std::shared_ptr<i2p::tunnel::TunnelPool> GetTunnelPool () { return m_Pool; };
bool IsReady () const { return m_LeaseSet && m_LeaseSet->HasNonExpiredLeases (); };
const i2p::data::LeaseSet * FindLeaseSet (const i2p::data::IdentHash& ident);
std::shared_ptr<const i2p::data::LeaseSet> FindLeaseSet (const i2p::data::IdentHash& ident);
bool RequestDestination (const i2p::data::IdentHash& dest, RequestComplete requestComplete = nullptr);
// streaming
i2p::stream::StreamingDestination * GetStreamingDestination () const { return m_StreamingDestination; };
void CreateStream (StreamRequestComplete streamRequestComplete, const std::string& dest, int port = 0);
void CreateStream (StreamRequestComplete streamRequestComplete, const i2p::data::IdentHash& dest, int port = 0);
std::shared_ptr<i2p::stream::Stream> CreateStream (const i2p::data::LeaseSet& remote, int port = 0);
std::shared_ptr<i2p::stream::Stream> CreateStream (std::shared_ptr<const i2p::data::LeaseSet> remote, int port = 0);
void AcceptStreams (const i2p::stream::StreamingDestination::Acceptor& acceptor);
void StopAcceptingStreams ();
bool IsAcceptingStreams () const;
@@ -85,7 +86,7 @@ namespace client
// implements GarlicDestination
const i2p::data::LeaseSet * GetLeaseSet ();
void HandleI2NPMessage (const uint8_t * buf, size_t len, i2p::tunnel::InboundTunnel * from);
void HandleI2NPMessage (const uint8_t * buf, size_t len, std::shared_ptr<i2p::tunnel::InboundTunnel> from);
// override GarlicDestination
bool SubmitSessionKey (const uint8_t * key, const uint8_t * tag);
@@ -109,6 +110,8 @@ namespace client
void RequestLeaseSet (const i2p::data::IdentHash& dest, RequestComplete requestComplete);
bool SendLeaseSetRequest (const i2p::data::IdentHash& dest, std::shared_ptr<const i2p::data::RouterInfo> nextFloodfill, LeaseSetRequest * request);
void HandleRequestTimoutTimer (const boost::system::error_code& ecode, const i2p::data::IdentHash& dest);
void HandleCleanupTimer (const boost::system::error_code& ecode);
void CleanupRemoteLeaseSets ();
private:
@@ -118,10 +121,10 @@ namespace client
boost::asio::io_service::work m_Work;
i2p::data::PrivateKeys m_Keys;
uint8_t m_EncryptionPublicKey[256], m_EncryptionPrivateKey[256];
std::map<i2p::data::IdentHash, i2p::data::LeaseSet *> m_RemoteLeaseSets;
std::map<i2p::data::IdentHash, std::shared_ptr<i2p::data::LeaseSet> > m_RemoteLeaseSets;
std::map<i2p::data::IdentHash, LeaseSetRequest *> m_LeaseSetRequests;
i2p::tunnel::TunnelPool * m_Pool;
std::shared_ptr<i2p::tunnel::TunnelPool> m_Pool;
i2p::data::LeaseSet * m_LeaseSet;
bool m_IsPublic;
uint32_t m_PublishReplyToken;
@@ -130,7 +133,7 @@ namespace client
i2p::stream::StreamingDestination * m_StreamingDestination;
i2p::datagram::DatagramDestination * m_DatagramDestination;
boost::asio::deadline_timer m_PublishConfirmationTimer;
boost::asio::deadline_timer m_PublishConfirmationTimer, m_CleanupTimer;
public:

View File

@@ -15,7 +15,7 @@ namespace i2p
namespace garlic
{
GarlicRoutingSession::GarlicRoutingSession (GarlicDestination * owner,
const i2p::data::RoutingDestination * destination, int numTags):
std::shared_ptr<const i2p::data::RoutingDestination> destination, int numTags):
m_Owner (owner), m_Destination (destination), m_NumTags (numTags),
m_LeaseSetUpdated (numTags > 0)
{
@@ -67,18 +67,34 @@ namespace garlic
m_UnconfirmedTagsMsgs.erase (it);
delete tags;
}
CleanupExpiredTags ();
}
bool GarlicRoutingSession::CleanupExpiredTags ()
{
uint32_t ts = i2p::util::GetSecondsSinceEpoch ();
for (auto it = m_SessionTags.begin (); it != m_SessionTags.end ();)
{
if (ts >= it->creationTime + OUTGOING_TAGS_EXPIRATION_TIMEOUT)
it = m_SessionTags.erase (it);
else
it++;
}
// delete expired unconfirmed tags
for (auto it = m_UnconfirmedTagsMsgs.begin (); it != m_UnconfirmedTagsMsgs.end ();)
{
if (ts >= it->second->tagsCreationTime + OUTGOING_TAGS_EXPIRATION_TIMEOUT)
{
if (m_Owner)
m_Owner->RemoveCreatedSession (it->first);
delete it->second;
it = m_UnconfirmedTagsMsgs.erase (it);
}
else
it++;
}
}
return !m_SessionTags.empty () || m_UnconfirmedTagsMsgs.empty ();
}
I2NPMessage * GarlicRoutingSession::WrapSingleMessage (I2NPMessage * msg)
{
@@ -149,7 +165,7 @@ namespace garlic
size_t GarlicRoutingSession::CreateAESBlock (uint8_t * buf, const I2NPMessage * msg)
{
size_t blockSize = 0;
bool createNewTags = m_Owner && m_NumTags && ((int)m_SessionTags.size () <= m_NumTags/2);
bool createNewTags = m_Owner && m_NumTags && ((int)m_SessionTags.size () <= m_NumTags*2/3);
UnconfirmedTags * newTags = createNewTags ? GenerateSessionTags () : nullptr;
htobuf16 (buf, newTags ? htobe16 (newTags->numTags) : 0); // tag count
blockSize += 2;
@@ -197,7 +213,7 @@ namespace garlic
{
(*numCloves)++;
m_UnconfirmedTagsMsgs[msgID] = newTags;
m_Owner->DeliveryStatusSent (this, msgID);
m_Owner->DeliveryStatusSent (shared_from_this (), msgID);
}
else
LogPrint ("DeliveryStatus clove was not created");
@@ -306,9 +322,6 @@ namespace garlic
GarlicDestination::~GarlicDestination ()
{
for (auto it: m_Sessions)
delete it.second;
m_Sessions.clear ();
}
void GarlicDestination::AddSessionKey (const uint8_t * key, const uint8_t * tag)
@@ -387,7 +400,7 @@ namespace garlic
}
void GarlicDestination::HandleAESBlock (uint8_t * buf, size_t len, std::shared_ptr<i2p::crypto::CBCDecryption> decryption,
i2p::tunnel::InboundTunnel * from)
std::shared_ptr<i2p::tunnel::InboundTunnel> from)
{
uint16_t tagCount = bufbe16toh (buf);
buf += 2; len -= 2;
@@ -426,7 +439,7 @@ namespace garlic
HandleGarlicPayload (buf, payloadSize, from);
}
void GarlicDestination::HandleGarlicPayload (uint8_t * buf, size_t len, i2p::tunnel::InboundTunnel * from)
void GarlicDestination::HandleGarlicPayload (uint8_t * buf, size_t len, std::shared_ptr<i2p::tunnel::InboundTunnel> from)
{
int numCloves = buf[0];
LogPrint (numCloves," cloves");
@@ -462,7 +475,7 @@ namespace garlic
buf += 32;
uint32_t gwTunnel = bufbe32toh (buf);
buf += 4;
i2p::tunnel::OutboundTunnel * tunnel = nullptr;
std::shared_ptr<i2p::tunnel::OutboundTunnel> tunnel;
if (from && from->GetTunnelPool ())
tunnel = from->GetTunnelPool ()->GetNextOutboundTunnel ();
if (tunnel) // we have send it through an outbound tunnel
@@ -488,7 +501,7 @@ namespace garlic
}
}
I2NPMessage * GarlicDestination::WrapMessage (const i2p::data::RoutingDestination& destination,
I2NPMessage * GarlicDestination::WrapMessage (std::shared_ptr<const i2p::data::RoutingDestination> destination,
I2NPMessage * msg, bool attachLeaseSet)
{
if (attachLeaseSet) // we should maintain this session
@@ -498,28 +511,48 @@ namespace garlic
}
else // one time session
{
GarlicRoutingSession session (this, &destination, 0); // don't use tag if no LeaseSet
GarlicRoutingSession session (this, destination, 0); // don't use tag if no LeaseSet
return session.WrapSingleMessage (msg);
}
}
GarlicRoutingSession * GarlicDestination::GetRoutingSession (
const i2p::data::RoutingDestination& destination, int numTags)
std::shared_ptr<GarlicRoutingSession> GarlicDestination::GetRoutingSession (
std::shared_ptr<const i2p::data::RoutingDestination> destination, int numTags)
{
auto it = m_Sessions.find (destination.GetIdentHash ());
GarlicRoutingSession * session = nullptr;
auto it = m_Sessions.find (destination->GetIdentHash ());
std::shared_ptr<GarlicRoutingSession> session;
if (it != m_Sessions.end ())
session = it->second;
if (!session)
{
session = new GarlicRoutingSession (this, &destination, numTags);
session = std::make_shared<GarlicRoutingSession> (this, destination, numTags);
std::unique_lock<std::mutex> l(m_SessionsMutex);
m_Sessions[destination.GetIdentHash ()] = session;
m_Sessions[destination->GetIdentHash ()] = session;
}
return session;
}
void GarlicDestination::DeliveryStatusSent (GarlicRoutingSession * session, uint32_t msgID)
void GarlicDestination::CleanupRoutingSessions ()
{
std::unique_lock<std::mutex> l(m_SessionsMutex);
for (auto it = m_Sessions.begin (); it != m_Sessions.end ();)
{
if (!it->second->CleanupExpiredTags ())
{
LogPrint (eLogInfo, "Routing session to ", it->first.ToBase32 (), " deleted");
it = m_Sessions.erase (it);
}
else
it++;
}
}
void GarlicDestination::RemoveCreatedSession (uint32_t msgID)
{
m_CreatedSessions.erase (msgID);
}
void GarlicDestination::DeliveryStatusSent (std::shared_ptr<GarlicRoutingSession> session, uint32_t msgID)
{
m_CreatedSessions[msgID] = session;
}
@@ -533,7 +566,7 @@ namespace garlic
{
it->second->TagsConfirmed (msgID);
m_CreatedSessions.erase (it);
LogPrint ("Garlic message ", msgID, " acknowledged");
LogPrint (eLogInfo, "Garlic message ", msgID, " acknowledged");
}
}
DeleteI2NPMessage (msg);

View File

@@ -54,7 +54,7 @@ namespace garlic
};
class GarlicDestination;
class GarlicRoutingSession
class GarlicRoutingSession: public std::enable_shared_from_this<GarlicRoutingSession>
{
struct UnconfirmedTags
{
@@ -67,11 +67,12 @@ namespace garlic
public:
GarlicRoutingSession (GarlicDestination * owner, const i2p::data::RoutingDestination * destination, int numTags);
GarlicRoutingSession (GarlicDestination * owner, std::shared_ptr<const i2p::data::RoutingDestination> destination, int numTags);
GarlicRoutingSession (const uint8_t * sessionKey, const SessionTag& sessionTag); // one time encryption
~GarlicRoutingSession ();
I2NPMessage * WrapSingleMessage (I2NPMessage * msg);
void TagsConfirmed (uint32_t msgID);
bool CleanupExpiredTags (); // returns true if something left
void SetLeaseSetUpdated () { m_LeaseSetUpdated = true; };
@@ -87,7 +88,7 @@ namespace garlic
private:
GarlicDestination * m_Owner;
const i2p::data::RoutingDestination * m_Destination;
std::shared_ptr<const i2p::data::RoutingDestination> m_Destination;
i2p::crypto::AESKey m_SessionKey;
std::list<SessionTag> m_SessionTags;
int m_NumTags;
@@ -105,20 +106,22 @@ namespace garlic
GarlicDestination (): m_LastTagsCleanupTime (0) {};
~GarlicDestination ();
GarlicRoutingSession * GetRoutingSession (const i2p::data::RoutingDestination& destination, int numTags);
I2NPMessage * WrapMessage (const i2p::data::RoutingDestination& destination,
std::shared_ptr<GarlicRoutingSession> GetRoutingSession (std::shared_ptr<const i2p::data::RoutingDestination> destination, int numTags);
void CleanupRoutingSessions ();
void RemoveCreatedSession (uint32_t msgID);
I2NPMessage * WrapMessage (std::shared_ptr<const i2p::data::RoutingDestination> destination,
I2NPMessage * msg, bool attachLeaseSet = false);
void AddSessionKey (const uint8_t * key, const uint8_t * tag); // one tag
virtual bool SubmitSessionKey (const uint8_t * key, const uint8_t * tag); // from different thread
void DeliveryStatusSent (GarlicRoutingSession * session, uint32_t msgID);
void DeliveryStatusSent (std::shared_ptr<GarlicRoutingSession> session, uint32_t msgID);
virtual void ProcessGarlicMessage (I2NPMessage * msg);
virtual void ProcessDeliveryStatusMessage (I2NPMessage * msg);
virtual void SetLeaseSetUpdated ();
virtual const i2p::data::LeaseSet * GetLeaseSet () = 0; // TODO
virtual void HandleI2NPMessage (const uint8_t * buf, size_t len, i2p::tunnel::InboundTunnel * from) = 0;
virtual void HandleI2NPMessage (const uint8_t * buf, size_t len, std::shared_ptr<i2p::tunnel::InboundTunnel> from) = 0;
protected:
@@ -128,19 +131,19 @@ namespace garlic
private:
void HandleAESBlock (uint8_t * buf, size_t len, std::shared_ptr<i2p::crypto::CBCDecryption> decryption,
i2p::tunnel::InboundTunnel * from);
void HandleGarlicPayload (uint8_t * buf, size_t len, i2p::tunnel::InboundTunnel * from);
std::shared_ptr<i2p::tunnel::InboundTunnel> from);
void HandleGarlicPayload (uint8_t * buf, size_t len, std::shared_ptr<i2p::tunnel::InboundTunnel> from);
private:
// outgoing sessions
std::mutex m_SessionsMutex;
std::map<i2p::data::IdentHash, GarlicRoutingSession *> m_Sessions;
std::map<i2p::data::IdentHash, std::shared_ptr<GarlicRoutingSession> > m_Sessions;
// incoming
std::map<SessionTag, std::shared_ptr<i2p::crypto::CBCDecryption>> m_Tags;
uint32_t m_LastTagsCleanupTime;
// DeliveryStatus
std::map<uint32_t, GarlicRoutingSession *> m_CreatedSessions; // msgID -> session
std::map<uint32_t, std::shared_ptr<GarlicRoutingSession> > m_CreatedSessions; // msgID -> session
};
}
}

View File

@@ -1,87 +1,279 @@
#include <cstring>
#include <cassert>
#include <boost/lexical_cast.hpp>
#include <boost/regex.hpp>
#include "ClientContext.h"
#include <string>
#include <atomic>
#include "HTTPProxy.h"
#include "util.h"
#include "Identity.h"
#include "Streaming.h"
#include "Destination.h"
#include "ClientContext.h"
#include "I2PEndian.h"
#include "I2PTunnel.h"
namespace i2p
{
namespace proxy
{
void HTTPProxyConnection::parseHeaders(const std::string& h, std::vector<header>& hm) {
std::string str (h);
std::string::size_type idx;
std::string t;
int i = 0;
while( (idx=str.find ("\r\n")) != std::string::npos) {
t=str.substr (0,idx);
str.erase (0,idx+2);
if (t == "")
break;
idx=t.find(": ");
if (idx == std::string::npos)
{
std::cout << "Bad header line: " << t << std::endl;
break;
}
LogPrint ("Name: ", t.substr (0,idx), " Value: ", t.substr (idx+2));
hm[i].name = t.substr (0,idx);
hm[i].value = t.substr (idx+2);
i++;
static const size_t http_buffer_size = 8192;
class HTTPProxyHandler: public i2p::client::I2PServiceHandler, public std::enable_shared_from_this<HTTPProxyHandler> {
private:
enum state {
GET_METHOD,
GET_HOSTNAME,
GET_HTTPV,
GET_HTTPVNL, //TODO: fallback to finding HOst: header if needed
DONE
};
void EnterState(state nstate);
bool HandleData(uint8_t *http_buff, std::size_t len);
void HandleSockRecv(const boost::system::error_code & ecode, std::size_t bytes_transfered);
void Terminate();
void AsyncSockRead();
void HTTPRequestFailed(/*std::string message*/);
void ExtractRequest();
bool ValidateHTTPRequest();
void HandleJumpServices();
bool CreateHTTPRequest(uint8_t *http_buff, std::size_t len);
void SentHTTPFailed(const boost::system::error_code & ecode);
void HandleStreamRequestComplete (std::shared_ptr<i2p::stream::Stream> stream);
uint8_t m_http_buff[http_buffer_size];
boost::asio::ip::tcp::socket * m_sock;
std::string m_request; //Data left to be sent
std::string m_url; //URL
std::string m_method; //Method
std::string m_version; //HTTP version
std::string m_address; //Address
std::string m_path; //Path
int m_port; //Port
state m_state;//Parsing state
public:
HTTPProxyHandler(HTTPProxyServer * parent, boost::asio::ip::tcp::socket * sock) :
I2PServiceHandler(parent), m_sock(sock)
{ EnterState(GET_METHOD); }
~HTTPProxyHandler() { Terminate(); }
void Handle () { AsyncSockRead(); }
};
void HTTPProxyHandler::AsyncSockRead()
{
LogPrint(eLogDebug,"--- HTTP Proxy async sock read");
if(m_sock) {
m_sock->async_receive(boost::asio::buffer(m_http_buff, http_buffer_size),
std::bind(&HTTPProxyHandler::HandleSockRecv, shared_from_this(),
std::placeholders::_1, std::placeholders::_2));
} else {
LogPrint(eLogError,"--- HTTP Proxy no socket for read");
}
}
void HTTPProxyConnection::ExtractRequest(request& r)
void HTTPProxyHandler::Terminate() {
if (Kill()) return;
if (m_sock) {
LogPrint(eLogDebug,"--- HTTP Proxy close sock");
m_sock->close();
delete m_sock;
m_sock = nullptr;
}
Done(shared_from_this());
}
/* All hope is lost beyond this point */
//TODO: handle this apropriately
void HTTPProxyHandler::HTTPRequestFailed(/*HTTPProxyHandler::errTypes error*/)
{
std::string requestString = m_Buffer;
int idx=requestString.find(" ");
std::string method = requestString.substr(0,idx);
requestString = requestString.substr(idx+1);
idx=requestString.find(" ");
std::string requestUrl = requestString.substr(0,idx);
LogPrint("method is: ", method, "\nRequest is: ", requestUrl);
std::string response = "HTTP/1.0 500 Internal Server Error\r\nContent-type: text/html\r\nContent-length: 0\r\n";
boost::asio::async_write(*m_sock, boost::asio::buffer(response,response.size()),
std::bind(&HTTPProxyHandler::SentHTTPFailed, shared_from_this(), std::placeholders::_1));
}
void HTTPProxyHandler::EnterState(HTTPProxyHandler::state nstate) {
m_state = nstate;
}
void HTTPProxyHandler::ExtractRequest()
{
LogPrint(eLogDebug,"--- HTTP Proxy method is: ", m_method, "\nRequest is: ", m_url);
std::string server="";
std::string port="80";
boost::regex rHTTP("http://(.*?)(:(\\d+))?(/.*)");
boost::smatch m;
std::string path;
if(boost::regex_search(requestUrl, m, rHTTP, boost::match_extra)) {
if(boost::regex_search(m_url, m, rHTTP, boost::match_extra)) {
server=m[1].str();
if(m[2].str() != "") {
port=m[3].str();
}
path=m[4].str();
}
LogPrint("server is: ",server, " port is: ", port, "\n path is: ",path);
r.uri = path;
r.method = method;
r.host = server;
r.port = boost::lexical_cast<int>(port);
LogPrint(eLogDebug,"--- HTTP Proxy server is: ",server, " port is: ", port, "\n path is: ",path);
m_address = server;
m_port = boost::lexical_cast<int>(port);
m_path = path;
}
bool HTTPProxyHandler::ValidateHTTPRequest() {
if ( m_version != "HTTP/1.0" && m_version != "HTTP/1.1" ) {
LogPrint(eLogError,"--- HTTP Proxy unsupported version: ", m_version);
HTTPRequestFailed(); //TODO: send right stuff
return false;
}
return true;
}
void HTTPProxyConnection::RunRequest()
{
request r;
ExtractRequest(r);
parseHeaders(m_Buffer, r.headers);
size_t addressHelperPos = r.uri.find ("i2paddresshelper");
if (addressHelperPos != std::string::npos)
void HTTPProxyHandler::HandleJumpServices() {
static const char * helpermark1 = "?i2paddresshelper=";
static const char * helpermark2 = "&i2paddresshelper=";
size_t addressHelperPos1 = m_path.rfind (helpermark1);
size_t addressHelperPos2 = m_path.rfind (helpermark2);
size_t addressHelperPos;
if (addressHelperPos1 == std::string::npos)
{
// jump service
size_t addressPos = r.uri.find ("=", addressHelperPos);
if (addressPos != std::string::npos)
{
LogPrint ("Jump service for ", r.host, " found. Inserting to address book");
auto base64 = r.uri.substr (addressPos + 1);
i2p::client::context.GetAddressBook ().InsertAddress (r.host, base64);
if (addressHelperPos2 == std::string::npos)
return; //Not a jump service
else
addressHelperPos = addressHelperPos2;
}
else
{
if (addressHelperPos2 == std::string::npos)
addressHelperPos = addressHelperPos1;
else if ( addressHelperPos1 > addressHelperPos2 )
addressHelperPos = addressHelperPos1;
else
addressHelperPos = addressHelperPos2;
}
auto base64 = m_path.substr (addressHelperPos + strlen(helpermark1));
base64 = i2p::util::http::urlDecode(base64); //Some of the symbols may be urlencoded
LogPrint (eLogDebug,"Jump service for ", m_address, " found at ", base64, ". Inserting to address book");
//TODO: this is very dangerous and broken. We should ask the user before doing anything see http://pastethis.i2p/raw/pn5fL4YNJL7OSWj3Sc6N/
//TODO: we could redirect the user again to avoid dirtiness in the browser
i2p::client::context.GetAddressBook ().InsertAddress (m_address, base64);
m_path.erase(addressHelperPos);
}
bool HTTPProxyHandler::CreateHTTPRequest(uint8_t *http_buff, std::size_t len) {
ExtractRequest(); //TODO: parse earlier
if (!ValidateHTTPRequest()) return false;
HandleJumpServices();
m_request = m_method;
m_request.push_back(' ');
m_request += m_path;
m_request.push_back(' ');
m_request += m_version;
m_request.push_back('\r');
m_request.push_back('\n');
m_request.append("Connection: close\r\n");
m_request.append(reinterpret_cast<const char *>(http_buff),len);
return true;
}
bool HTTPProxyHandler::HandleData(uint8_t *http_buff, std::size_t len)
{
assert(len); // This should always be called with a least a byte left to parse
while (len > 0) {
//TODO: fallback to finding HOst: header if needed
switch (m_state) {
case GET_METHOD:
switch (*http_buff) {
case ' ': EnterState(GET_HOSTNAME); break;
default: m_method.push_back(*http_buff); break;
}
break;
case GET_HOSTNAME:
switch (*http_buff) {
case ' ': EnterState(GET_HTTPV); break;
default: m_url.push_back(*http_buff); break;
}
break;
case GET_HTTPV:
switch (*http_buff) {
case '\r': EnterState(GET_HTTPVNL); break;
default: m_version.push_back(*http_buff); break;
}
break;
case GET_HTTPVNL:
switch (*http_buff) {
case '\n': EnterState(DONE); break;
default:
LogPrint(eLogError,"--- HTTP Proxy rejected invalid request ending with: ", ((int)*http_buff));
HTTPRequestFailed(); //TODO: add correct code
return false;
}
break;
default:
LogPrint(eLogError,"--- HTTP Proxy invalid state: ", m_state);
HTTPRequestFailed(); //TODO: add correct code 500
return false;
}
}
http_buff++;
len--;
if (m_state == DONE)
return CreateHTTPRequest(http_buff,len);
}
return true;
}
void HTTPProxyHandler::HandleSockRecv(const boost::system::error_code & ecode, std::size_t len)
{
LogPrint(eLogDebug,"--- HTTP Proxy sock recv: ", len);
if(ecode) {
LogPrint(eLogWarning," --- HTTP Proxy sock recv got error: ", ecode);
Terminate();
return;
}
if (HandleData(m_http_buff, len)) {
if (m_state == DONE) {
LogPrint(eLogInfo,"--- HTTP Proxy requested: ", m_url);
GetOwner()->CreateStream (std::bind (&HTTPProxyHandler::HandleStreamRequestComplete,
shared_from_this(), std::placeholders::_1), m_address, m_port);
} else {
AsyncSockRead();
}
}
}
void HTTPProxyHandler::SentHTTPFailed(const boost::system::error_code & ecode)
{
if (!ecode) {
Terminate();
} else {
LogPrint (eLogError,"--- HTTP Proxy Closing socket after sending failure because: ", ecode.message ());
Terminate();
}
}
void HTTPProxyHandler::HandleStreamRequestComplete (std::shared_ptr<i2p::stream::Stream> stream)
{
if (stream) {
if (Kill()) return;
LogPrint (eLogInfo,"--- HTTP Proxy New I2PTunnel connection");
auto connection = std::make_shared<i2p::client::I2PTunnelConnection>(GetOwner(), m_sock, stream);
GetOwner()->AddHandler (connection);
connection->I2PConnect (reinterpret_cast<const uint8_t*>(m_request.data()), m_request.size());
Done(shared_from_this());
} else {
LogPrint (eLogError,"--- HTTP Proxy Issue when creating the stream, check the previous warnings for more info.");
HTTPRequestFailed(); // TODO: Send correct error message host unreachable
}
}
HTTPProxyServer::HTTPProxyServer(int port):
TCPIPAcceptor(port, i2p::client::context.GetSharedLocalDestination ())
{
}
LogPrint("Requesting ", r.host, ":", r.port, " with path ", r.uri, " and method ", r.method);
SendToAddress (r.host, r.port, m_Buffer, m_BufferLen);
std::shared_ptr<i2p::client::I2PServiceHandler> HTTPProxyServer::CreateHandler(boost::asio::ip::tcp::socket * socket)
{
return std::make_shared<HTTPProxyHandler> (this, socket);
}
}
}

View File

@@ -1,42 +1,30 @@
#ifndef HTTP_PROXY_H__
#define HTTP_PROXY_H__
#include <sstream>
#include <thread>
#include <memory>
#include <set>
#include <boost/asio.hpp>
#include <boost/array.hpp>
#include "HTTPServer.h"
#include <mutex>
#include "I2PService.h"
namespace i2p
{
namespace proxy
{
class HTTPProxyConnection : public i2p::util::HTTPConnection
class HTTPProxyServer: public i2p::client::TCPIPAcceptor
{
public:
HTTPProxyConnection (boost::asio::ip::tcp::socket * socket): HTTPConnection(socket) { };
protected:
void RunRequest();
void parseHeaders(const std::string& h, std::vector<header>& hm);
void ExtractRequest(request& r);
};
// Implements TCPIPAcceptor
std::shared_ptr<i2p::client::I2PServiceHandler> CreateHandler(boost::asio::ip::tcp::socket * socket);
const char* GetName() { return "HTTP Proxy"; }
class HTTPProxy : public i2p::util::HTTPServer
{
public:
HTTPProxy (int port): HTTPServer(port) {};
private:
void CreateConnection(boost::asio::ip::tcp::socket * m_NewSocket)
{
new HTTPProxyConnection(m_NewSocket);
}
HTTPProxyServer(int port);
~HTTPProxyServer() {}
};
typedef HTTPProxyServer HTTPProxy;
}
}
#endif
#endif

View File

@@ -469,6 +469,9 @@ namespace util
const char HTTP_COMMAND_LOCAL_DESTINATIONS[] = "local_destinations";
const char HTTP_COMMAND_LOCAL_DESTINATION[] = "local_destination";
const char HTTP_PARAM_BASE32_ADDRESS[] = "b32";
const char HTTP_COMMAND_SAM_SESSIONS[] = "sam_sessions";
const char HTTP_COMMAND_SAM_SESSION[] = "sam_session";
const char HTTP_PARAM_SAM_SESSION_ID[] = "id";
namespace misc_strings
{
@@ -605,7 +608,8 @@ namespace util
{
if (ecode != boost::asio::error::operation_aborted)
{
m_Socket->close ();
boost::system::error_code ignored_ec;
m_Socket->shutdown(boost::asio::ip::tcp::socket::shutdown_both, ignored_ec);
Terminate ();
}
}
@@ -672,7 +676,10 @@ namespace util
s << "<br><b><a href=/?" << HTTP_COMMAND_LOCAL_DESTINATIONS << ">Local destinations</a></b>";
s << "<br><b><a href=/?" << HTTP_COMMAND_TUNNELS << ">Tunnels</a></b>";
s << "<br><b><a href=/?" << HTTP_COMMAND_TRANSIT_TUNNELS << ">Transit tunnels</a></b>";
s << "<br><b><a href=/?" << HTTP_COMMAND_TRANSPORTS << ">Transports</a></b><br>";
s << "<br><b><a href=/?" << HTTP_COMMAND_TRANSPORTS << ">Transports</a></b>";
if (i2p::client::context.GetSAMBridge ())
s << "<br><b><a href=/?" << HTTP_COMMAND_SAM_SESSIONS << ">SAM sessions</a></b>";
s << "<br>";
if (i2p::context.AcceptsTunnels ())
s << "<br><b><a href=/?" << HTTP_COMMAND_STOP_ACCEPTING_TUNNELS << ">Stop accepting tunnels</a></b><br>";
@@ -705,26 +712,39 @@ namespace util
auto b32 = params[HTTP_PARAM_BASE32_ADDRESS];
ShowLocalDestination (b32, s);
}
else if (cmd == HTTP_COMMAND_SAM_SESSIONS)
ShowSAMSessions (s);
else if (cmd == HTTP_COMMAND_SAM_SESSION)
{
std::map<std::string, std::string> params;
ExtractParams (command.substr (paramsPos), params);
auto id = params[HTTP_PARAM_SAM_SESSION_ID];
ShowSAMSession (id, s);
}
}
void HTTPConnection::ShowTransports (std::stringstream& s)
{
s << "NTCP<br>";
for (auto it: i2p::transport::transports.GetNTCPSessions ())
{
if (it.second && it.second->IsEstablished ())
auto ntcpServer = i2p::transport::transports.GetNTCPServer ();
if (ntcpServer)
{
s << "NTCP<br>";
for (auto it: ntcpServer->GetNTCPSessions ())
{
// incoming connection doesn't have remote RI
auto outgoing = it.second->GetRemoteRouter ();
if (outgoing) s << "-->";
s << it.second->GetRemoteIdentity ().GetIdentHash ().ToBase64 ().substr (0, 4) << ": "
<< it.second->GetSocket ().remote_endpoint().address ().to_string ();
if (!outgoing) s << "-->";
s << " [" << it.second->GetNumSentBytes () << ":" << it.second->GetNumReceivedBytes () << "]";
s << "<br>";
if (it.second && it.second->IsEstablished ())
{
// incoming connection doesn't have remote RI
auto outgoing = it.second->GetRemoteRouter ();
if (outgoing) s << "-->";
s << it.second->GetRemoteIdentity ().GetIdentHash ().ToBase64 ().substr (0, 4) << ": "
<< it.second->GetSocket ().remote_endpoint().address ().to_string ();
if (!outgoing) s << "-->";
s << " [" << it.second->GetNumSentBytes () << ":" << it.second->GetNumReceivedBytes () << "]";
s << "<br>";
}
s << std::endl;
}
s << std::endl;
}
}
auto ssuServer = i2p::transport::transports.GetSSUServer ();
if (ssuServer)
{
@@ -738,6 +758,8 @@ namespace util
s << endpoint.address ().to_string () << ":" << endpoint.port ();
if (!outgoing) s << "-->";
s << " [" << it.second->GetNumSentBytes () << ":" << it.second->GetNumReceivedBytes () << "]";
if (it.second->GetRelayTag ())
s << " [itag:" << it.second->GetRelayTag () << "]";
s << "<br>";
s << std::endl;
}
@@ -746,6 +768,8 @@ namespace util
void HTTPConnection::ShowTunnels (std::stringstream& s)
{
s << "Queue size:" << i2p::tunnel::tunnels.GetQueueSize () << "<br>";
for (auto it: i2p::tunnel::tunnels.GetOutboundTunnels ())
{
it->GetTunnelConfig ()->Print (s);
@@ -835,10 +859,57 @@ namespace util
s << it.first << "->" << i2p::client::context.GetAddressBook ().ToAddress(it.second->GetRemoteIdentity ()) << " ";
s << " [" << it.second->GetNumSentBytes () << ":" << it.second->GetNumReceivedBytes () << "]";
s << " [out:" << it.second->GetSendQueueSize () << "][in:" << it.second->GetReceiveQueueSize () << "]";
s << "[buf:" << it.second->GetSendBufferSize () << "]";
s << "[RTT:" << it.second->GetRTT () << "]";
s << "<br>"<< std::endl;
}
}
}
void HTTPConnection::ShowSAMSessions (std::stringstream& s)
{
auto sam = i2p::client::context.GetSAMBridge ();
if (sam)
{
for (auto& it: sam->GetSessions ())
{
s << "<a href=/?" << HTTP_COMMAND_SAM_SESSION;
s << "&" << HTTP_PARAM_SAM_SESSION_ID << "=" << it.first << ">";
s << it.first << "</a><br>" << std::endl;
}
}
}
void HTTPConnection::ShowSAMSession (const std::string& id, std::stringstream& s)
{
auto sam = i2p::client::context.GetSAMBridge ();
if (sam)
{
auto session = sam->FindSession (id);
if (session)
{
for (auto it: session->sockets)
{
switch (it->GetSocketType ())
{
case i2p::client::eSAMSocketTypeSession:
s << "session";
break;
case i2p::client::eSAMSocketTypeStream:
s << "stream";
break;
case i2p::client::eSAMSocketTypeAcceptor:
s << "acceptor";
break;
default:
s << "unknown";
}
s << " [" << it->GetSocket ().remote_endpoint() << "]";
s << "<br>" << std::endl;
}
}
}
}
void HTTPConnection::StartAcceptingTunnels (std::stringstream& s)
{
@@ -897,10 +968,10 @@ namespace util
}
}
void HTTPConnection::SendToDestination (const i2p::data::LeaseSet * remote, int port, const char * buf, size_t len)
void HTTPConnection::SendToDestination (std::shared_ptr<const i2p::data::LeaseSet> remote, int port, const char * buf, size_t len)
{
if (!m_Stream)
m_Stream = i2p::client::context.GetSharedLocalDestination ()->CreateStream (*remote, port);
m_Stream = i2p::client::context.GetSharedLocalDestination ()->CreateStream (remote, port);
if (m_Stream)
{
m_Stream->Send ((uint8_t *)buf, len);

View File

@@ -69,6 +69,8 @@ namespace util
void ShowTransitTunnels (std::stringstream& s);
void ShowLocalDestinations (std::stringstream& s);
void ShowLocalDestination (const std::string& b32, std::stringstream& s);
void ShowSAMSessions (std::stringstream& s);
void ShowSAMSession (const std::string& id, std::stringstream& s);
void StartAcceptingTunnels (std::stringstream& s);
void StopAcceptingTunnels (std::stringstream& s);
void FillContent (std::stringstream& s);
@@ -93,7 +95,7 @@ namespace util
void SendToAddress (const std::string& address, int port, const char * buf, size_t len);
void HandleDestinationRequestTimeout (const boost::system::error_code& ecode,
i2p::data::IdentHash destination, int port, const char * buf, size_t len);
void SendToDestination (const i2p::data::LeaseSet * remote, int port, const char * buf, size_t len);
void SendToDestination (std::shared_ptr<const i2p::data::LeaseSet> remote, int port, const char * buf, size_t len);
public:

View File

@@ -71,7 +71,7 @@ namespace i2p
return msg;
}
I2NPMessage * CreateI2NPMessage (const uint8_t * buf, int len, i2p::tunnel::InboundTunnel * from)
I2NPMessage * CreateI2NPMessage (const uint8_t * buf, int len, std::shared_ptr<i2p::tunnel::InboundTunnel> from)
{
I2NPMessage * msg = NewI2NPMessage ();
memcpy (msg->GetBuffer (), buf, len);
@@ -82,7 +82,7 @@ namespace i2p
I2NPMessage * CreateDeliveryStatusMsg (uint32_t msgID)
{
I2NPMessage * m = NewI2NPMessage ();
I2NPMessage * m = NewI2NPShortMessage ();
uint8_t * buf = m->GetPayload ();
if (msgID)
{
@@ -108,10 +108,10 @@ namespace i2p
buf += 32;
memcpy (buf, from, 32); // from
buf += 32;
uint8_t flag = exploratory ? 0x0C : 0x08; // 1000 - RI, 1100 -exporatory
uint8_t flag = exploratory ? DATABASE_LOOKUP_TYPE_EXPLORATORY_LOOKUP : DATABASE_LOOKUP_TYPE_ROUTERINFO_LOOKUP;
if (replyTunnelID)
{
*buf = flag | 0x01; // set delivery flag
*buf = flag | DATABASE_LOOKUP_DELIVERY_FLAG; // set delivery flag
htobe32buf (buf+1, replyTunnelID);
buf += 5;
}
@@ -154,7 +154,7 @@ namespace i2p
buf += 32;
memcpy (buf, replyTunnel->GetNextIdentHash (), 32); // reply tunnel GW
buf += 32;
*buf = 7; // flags (01 - tunnel, 10 - encrypted, 0100 - LS lookup
*buf = DATABASE_LOOKUP_DELIVERY_FLAG | DATABASE_LOOKUP_ENCYPTION_FLAG | DATABASE_LOOKUP_TYPE_LEASESET_LOOKUP; // flags
htobe32buf (buf + 1, replyTunnel->GetNextTunnelID ()); // reply tunnel ID
buf += 5;
@@ -184,18 +184,18 @@ namespace i2p
I2NPMessage * CreateDatabaseSearchReply (const i2p::data::IdentHash& ident,
const i2p::data::RouterInfo * floodfill)
std::vector<i2p::data::IdentHash> routers)
{
I2NPMessage * m = NewI2NPShortMessage ();
uint8_t * buf = m->GetPayload ();
size_t len = 0;
memcpy (buf, ident, 32);
len += 32;
buf[len] = floodfill ? 1 : 0; // 1 router for now
buf[len] = routers.size ();
len++;
if (floodfill)
for (auto it: routers)
{
memcpy (buf + len, floodfill->GetIdentHash (), 32);
memcpy (buf + len, it, 32);
len += 32;
}
memcpy (buf + len, i2p::context.GetRouterInfo ().GetIdentHash (), 32);
@@ -205,7 +205,7 @@ namespace i2p
return m;
}
I2NPMessage * CreateDatabaseStoreMsg (const i2p::data::RouterInfo * router)
I2NPMessage * CreateDatabaseStoreMsg (const i2p::data::RouterInfo * router, uint32_t replyToken)
{
if (!router) // we send own RouterInfo
router = &context.GetRouterInfo ();
@@ -214,19 +214,27 @@ namespace i2p
uint8_t * payload = m->GetPayload ();
memcpy (payload + DATABASE_STORE_KEY_OFFSET, router->GetIdentHash (), 32);
payload[DATABASE_STORE_TYPE_OFFSET] = 0;
htobe32buf (payload + DATABASE_STORE_REPLY_TOKEN_OFFSET, 0);
payload[DATABASE_STORE_TYPE_OFFSET] = 0; // RouterInfo
htobe32buf (payload + DATABASE_STORE_REPLY_TOKEN_OFFSET, replyToken);
uint8_t * buf = payload + DATABASE_STORE_HEADER_SIZE;
if (replyToken)
{
memset (buf, 0, 4); // zero tunnelID means direct reply
buf += 4;
memcpy (buf, router->GetIdentHash (), 32);
buf += 32;
}
CryptoPP::Gzip compressor;
compressor.Put (router->GetBuffer (), router->GetBufferLen ());
compressor.MessageEnd();
auto size = compressor.MaxRetrievable ();
uint8_t * buf = payload + DATABASE_STORE_HEADER_SIZE;
htobe16buf (buf, size); // size
buf += 2;
// TODO: check if size doesn't exceed buffer
compressor.Get (buf, size);
m->len += DATABASE_STORE_HEADER_SIZE + 2 + size; // payload size
buf += size;
m->len += (buf - payload); // payload size
FillI2NPMessageHeader (m, eI2NPDatabaseStore);
return m;
@@ -312,7 +320,7 @@ namespace i2p
int num = buf[0];
LogPrint ("VariableTunnelBuild ", num, " records");
i2p::tunnel::Tunnel * tunnel = i2p::tunnel::tunnels.GetPendingTunnel (replyMsgID);
auto tunnel = i2p::tunnel::tunnels.GetPendingInboundTunnel (replyMsgID);
if (tunnel)
{
// endpoint of inbound tunnel
@@ -321,7 +329,7 @@ namespace i2p
{
LogPrint ("Inbound tunnel ", tunnel->GetTunnelID (), " has been created");
tunnel->SetState (i2p::tunnel::eTunnelStateEstablished);
i2p::tunnel::tunnels.AddInboundTunnel (static_cast<i2p::tunnel::InboundTunnel *>(tunnel));
i2p::tunnel::tunnels.AddInboundTunnel (tunnel);
}
else
{
@@ -373,7 +381,7 @@ namespace i2p
void HandleVariableTunnelBuildReplyMsg (uint32_t replyMsgID, uint8_t * buf, size_t len)
{
LogPrint ("VariableTunnelBuildReplyMsg replyMsgID=", replyMsgID);
i2p::tunnel::Tunnel * tunnel = i2p::tunnel::tunnels.GetPendingTunnel (replyMsgID);
auto tunnel = i2p::tunnel::tunnels.GetPendingOutboundTunnel (replyMsgID);
if (tunnel)
{
// reply for outbound tunnel
@@ -381,7 +389,7 @@ namespace i2p
{
LogPrint ("Outbound tunnel ", tunnel->GetTunnelID (), " has been created");
tunnel->SetState (i2p::tunnel::eTunnelStateEstablished);
i2p::tunnel::tunnels.AddOutboundTunnel (static_cast<i2p::tunnel::OutboundTunnel *>(tunnel));
i2p::tunnel::tunnels.AddOutboundTunnel (tunnel);
}
else
{
@@ -465,35 +473,6 @@ namespace i2p
FillI2NPMessageHeader (msg, eI2NPTunnelGateway); // gateway message
return msg;
}
void HandleTunnelGatewayMsg (I2NPMessage * msg)
{
const uint8_t * payload = msg->GetPayload ();
uint32_t tunnelID = bufbe32toh(payload + TUNNEL_GATEWAY_HEADER_TUNNELID_OFFSET);
uint16_t len = bufbe16toh(payload + TUNNEL_GATEWAY_HEADER_LENGTH_OFFSET);
// we make payload as new I2NP message to send
msg->offset += I2NP_HEADER_SIZE + TUNNEL_GATEWAY_HEADER_SIZE;
msg->len = msg->offset + len;
auto typeID = msg->GetTypeID ();
LogPrint ("TunnelGateway of ", (int)len, " bytes for tunnel ", (unsigned int)tunnelID, ". Msg type ", (int)typeID);
if (typeID == eI2NPDatabaseStore || typeID == eI2NPDatabaseSearchReply)
{
// transit DatabaseStore my contain new/updated RI
// or DatabaseSearchReply with new routers
auto ds = NewI2NPMessage ();
*ds = *msg;
i2p::data::netdb.PostI2NPMsg (ds);
}
i2p::tunnel::TransitTunnel * tunnel = i2p::tunnel::tunnels.GetTransitTunnel (tunnelID);
if (tunnel)
tunnel->SendTunnelDataMsg (msg);
else
{
LogPrint ("Tunnel ", (unsigned int)tunnelID, " not found");
i2p::DeleteI2NPMessage (msg);
}
}
size_t GetI2NPMessageLength (const uint8_t * msg)
{
@@ -543,7 +522,7 @@ namespace i2p
break;
case eI2NPTunnelGateway:
LogPrint ("TunnelGateway");
HandleTunnelGatewayMsg (msg);
i2p::tunnel::tunnels.PostTunnelData (msg);
break;
case eI2NPGarlic:
LogPrint ("Garlic");
@@ -573,10 +552,54 @@ namespace i2p
else
i2p::context.ProcessDeliveryStatusMessage (msg);
break;
case eI2NPVariableTunnelBuild:
case eI2NPVariableTunnelBuildReply:
case eI2NPTunnelBuild:
case eI2NPTunnelBuildReply:
// forward to tunnel thread
i2p::tunnel::tunnels.PostTunnelData (msg);
break;
default:
HandleI2NPMessage (msg->GetBuffer (), msg->GetLength ());
DeleteI2NPMessage (msg);
}
}
}
I2NPMessagesHandler::~I2NPMessagesHandler ()
{
Flush ();
}
void I2NPMessagesHandler::PutNextMessage (I2NPMessage * msg)
{
if (msg)
{
switch (msg->GetTypeID ())
{
case eI2NPTunnelData:
m_TunnelMsgs.push_back (msg);
break;
case eI2NPTunnelGateway:
m_TunnelGatewayMsgs.push_back (msg);
break;
default:
HandleI2NPMessage (msg);
}
}
}
void I2NPMessagesHandler::Flush ()
{
if (!m_TunnelMsgs.empty ())
{
i2p::tunnel::tunnels.PostTunnelData (m_TunnelMsgs);
m_TunnelMsgs.clear ();
}
if (!m_TunnelGatewayMsgs.empty ())
{
i2p::tunnel::tunnels.PostTunnelData (m_TunnelGatewayMsgs);
m_TunnelGatewayMsgs.clear ();
}
}
}

View File

@@ -2,9 +2,10 @@
#define I2NP_PROTOCOL_H__
#include <inttypes.h>
#include <set>
#include <cryptopp/sha.h>
#include <string.h>
#include <set>
#include <memory>
#include <cryptopp/sha.h>
#include "I2PEndian.h"
#include "Identity.h"
#include "RouterInfo.h"
@@ -87,6 +88,15 @@ namespace i2p
const int NUM_TUNNEL_BUILD_RECORDS = 8;
// DatabaseLookup flags
const uint8_t DATABASE_LOOKUP_DELIVERY_FLAG = 0x01;
const uint8_t DATABASE_LOOKUP_ENCYPTION_FLAG = 0x02;
const uint8_t DATABASE_LOOKUP_TYPE_FLAGS_MASK = 0x0C;
const uint8_t DATABASE_LOOKUP_TYPE_NORMAL_LOOKUP = 0;
const uint8_t DATABASE_LOOKUP_TYPE_LEASESET_LOOKUP = 0x04; // 0100
const uint8_t DATABASE_LOOKUP_TYPE_ROUTERINFO_LOOKUP = 0x08; // 1000
const uint8_t DATABASE_LOOKUP_TYPE_EXPLORATORY_LOOKUP = 0x0C; // 1100
namespace tunnel
{
class InboundTunnel;
@@ -99,7 +109,7 @@ namespace tunnel
{
uint8_t * buf;
size_t len, offset, maxLen;
i2p::tunnel::InboundTunnel * from;
std::shared_ptr<i2p::tunnel::InboundTunnel> from;
I2NPMessage (): buf (nullptr),len (I2NP_HEADER_SIZE + 2),
offset(2), maxLen (0), from (nullptr) {}; // reserve 2 bytes for NTCP header
@@ -186,7 +196,7 @@ namespace tunnel
void FillI2NPMessageHeader (I2NPMessage * msg, I2NPMessageType msgType, uint32_t replyMsgID = 0);
void RenewI2NPMessageHeader (I2NPMessage * msg);
I2NPMessage * CreateI2NPMessage (I2NPMessageType msgType, const uint8_t * buf, int len, uint32_t replyMsgID = 0);
I2NPMessage * CreateI2NPMessage (const uint8_t * buf, int len, i2p::tunnel::InboundTunnel * from = nullptr);
I2NPMessage * CreateI2NPMessage (const uint8_t * buf, int len, std::shared_ptr<i2p::tunnel::InboundTunnel> from = nullptr);
I2NPMessage * CreateDeliveryStatusMsg (uint32_t msgID);
I2NPMessage * CreateRouterInfoDatabaseLookupMsg (const uint8_t * key, const uint8_t * from,
@@ -194,9 +204,9 @@ namespace tunnel
I2NPMessage * CreateLeaseSetDatabaseLookupMsg (const i2p::data::IdentHash& dest,
const std::set<i2p::data::IdentHash>& excludedFloodfills,
const i2p::tunnel::InboundTunnel * replyTunnel, const uint8_t * replyKey, const uint8_t * replyTag);
I2NPMessage * CreateDatabaseSearchReply (const i2p::data::IdentHash& ident, const i2p::data::RouterInfo * floodfill);
I2NPMessage * CreateDatabaseSearchReply (const i2p::data::IdentHash& ident, std::vector<i2p::data::IdentHash> routers);
I2NPMessage * CreateDatabaseStoreMsg (const i2p::data::RouterInfo * router = nullptr);
I2NPMessage * CreateDatabaseStoreMsg (const i2p::data::RouterInfo * router = nullptr, uint32_t replyToken = 0);
I2NPMessage * CreateDatabaseStoreMsg (const i2p::data::LeaseSet * leaseSet, uint32_t replyToken = 0);
bool HandleBuildRequestRecords (int num, uint8_t * records, uint8_t * clearText);
@@ -207,7 +217,6 @@ namespace tunnel
I2NPMessage * CreateTunnelDataMsg (const uint8_t * buf);
I2NPMessage * CreateTunnelDataMsg (uint32_t tunnelID, const uint8_t * payload);
void HandleTunnelGatewayMsg (I2NPMessage * msg);
I2NPMessage * CreateTunnelGatewayMsg (uint32_t tunnelID, const uint8_t * buf, size_t len);
I2NPMessage * CreateTunnelGatewayMsg (uint32_t tunnelID, I2NPMessageType msgType,
const uint8_t * buf, size_t len, uint32_t replyMsgID = 0);
@@ -216,6 +225,19 @@ namespace tunnel
size_t GetI2NPMessageLength (const uint8_t * msg);
void HandleI2NPMessage (uint8_t * msg, size_t len);
void HandleI2NPMessage (I2NPMessage * msg);
class I2NPMessagesHandler
{
public:
~I2NPMessagesHandler ();
void PutNextMessage (I2NPMessage * msg);
void Flush ();
private:
std::vector<I2NPMessage *> m_TunnelMsgs, m_TunnelGatewayMsgs;
};
}
#endif

375
I2PControl.cpp Normal file
View File

@@ -0,0 +1,375 @@
// There is bug in boost 1.49 with gcc 4.7 coming with Debian Wheezy
#define GCC47_BOOST149 ((BOOST_VERSION == 104900) && (__GNUC__ == 4) && (__GNUC_MINOR__ == 7))
#include "I2PControl.h"
#include <sstream>
#include <boost/lexical_cast.hpp>
#include <boost/date_time/local_time/local_time.hpp>
#include <boost/date_time/posix_time/posix_time.hpp>
#include <boost/property_tree/ptree.hpp>
#if !GCC47_BOOST149
#include <boost/property_tree/json_parser.hpp>
#endif
#include "Log.h"
#include "NetDb.h"
#include "RouterContext.h"
#include "Daemon.h"
#include "Tunnel.h"
#include "Timestamp.h"
#include "Transports.h"
namespace i2p
{
namespace client
{
I2PControlService::I2PControlService (int port):
m_Password (I2P_CONTROL_DEFAULT_PASSWORD), m_IsRunning (false), m_Thread (nullptr),
m_Acceptor (m_Service, boost::asio::ip::tcp::endpoint(boost::asio::ip::tcp::v4(), port)),
m_ShutdownTimer (m_Service)
{
m_MethodHandlers[I2P_CONTROL_METHOD_AUTHENTICATE] = &I2PControlService::AuthenticateHandler;
m_MethodHandlers[I2P_CONTROL_METHOD_ECHO] = &I2PControlService::EchoHandler;
m_MethodHandlers[I2P_CONTROL_METHOD_I2PCONTROL] = &I2PControlService::I2PControlHandler;
m_MethodHandlers[I2P_CONTROL_METHOD_ROUTER_INFO] = &I2PControlService::RouterInfoHandler;
m_MethodHandlers[I2P_CONTROL_METHOD_ROUTER_MANAGER] = &I2PControlService::RouterManagerHandler;
m_MethodHandlers[I2P_CONTROL_METHOD_NETWORK_SETTING] = &I2PControlService::NetworkSettingHandler;
// RouterInfo
m_RouterInfoHandlers[I2P_CONTROL_ROUTER_INFO_NETDB_KNOWNPEERS] = &I2PControlService::NetDbKnownPeersHandler;
m_RouterInfoHandlers[I2P_CONTROL_ROUTER_INFO_NETDB_ACTIVEPEERS] = &I2PControlService::NetDbActivePeersHandler;
m_RouterInfoHandlers[I2P_CONTROL_ROUTER_INFO_TUNNELS_PARTICIPATING] = &I2PControlService::TunnelsParticipatingHandler;
// RouterManager
m_RouterManagerHandlers[I2P_CONTROL_ROUTER_MANAGER_SHUTDOWN] = &I2PControlService::ShutdownHandler;
m_RouterManagerHandlers[I2P_CONTROL_ROUTER_MANAGER_SHUTDOWN_GRACEFUL] = &I2PControlService::ShutdownGracefulHandler;
m_RouterManagerHandlers[I2P_CONTROL_ROUTER_MANAGER_RESEED] = &I2PControlService::ReseedHandler;
}
I2PControlService::~I2PControlService ()
{
Stop ();
}
void I2PControlService::Start ()
{
if (!m_IsRunning)
{
Accept ();
m_IsRunning = true;
m_Thread = new std::thread (std::bind (&I2PControlService::Run, this));
}
}
void I2PControlService::Stop ()
{
if (m_IsRunning)
{
m_IsRunning = false;
m_Acceptor.cancel ();
m_Service.stop ();
if (m_Thread)
{
m_Thread->join ();
delete m_Thread;
m_Thread = nullptr;
}
}
}
void I2PControlService::Run ()
{
while (m_IsRunning)
{
try
{
m_Service.run ();
}
catch (std::exception& ex)
{
LogPrint (eLogError, "I2PControl: ", ex.what ());
}
}
}
void I2PControlService::Accept ()
{
auto newSocket = std::make_shared<boost::asio::ip::tcp::socket> (m_Service);
m_Acceptor.async_accept (*newSocket, std::bind (&I2PControlService::HandleAccept, this,
std::placeholders::_1, newSocket));
}
void I2PControlService::HandleAccept(const boost::system::error_code& ecode, std::shared_ptr<boost::asio::ip::tcp::socket> socket)
{
if (ecode != boost::asio::error::operation_aborted)
Accept ();
if (!ecode)
{
LogPrint (eLogInfo, "New I2PControl request from ", socket->remote_endpoint ());
ReadRequest (socket);
}
else
LogPrint (eLogError, "I2PControl accept error: ", ecode.message ());
}
void I2PControlService::ReadRequest (std::shared_ptr<boost::asio::ip::tcp::socket> socket)
{
auto request = std::make_shared<I2PControlBuffer>();
socket->async_read_some (
#if BOOST_VERSION >= 104900
boost::asio::buffer (*request),
#else
boost::asio::buffer (request->data (), request->size ()),
#endif
std::bind(&I2PControlService::HandleRequestReceived, this,
std::placeholders::_1, std::placeholders::_2, socket, request));
}
void I2PControlService::HandleRequestReceived (const boost::system::error_code& ecode,
size_t bytes_transferred, std::shared_ptr<boost::asio::ip::tcp::socket> socket,
std::shared_ptr<I2PControlBuffer> buf)
{
if (ecode)
{
LogPrint (eLogError, "I2PControl read error: ", ecode.message ());
}
else
{
try
{
bool isHtml = !memcmp (buf->data (), "POST", 4);
std::stringstream ss;
ss.write (buf->data (), bytes_transferred);
if (isHtml)
{
std::string header;
while (!ss.eof () && header != "\r")
std::getline(ss, header);
if (ss.eof ())
{
LogPrint (eLogError, "Malformed I2PControl request. HTTP header expected");
return; // TODO:
}
}
#if GCC47_BOOST149
LogPrint (eLogError, "json_read is not supported due bug in boost 1.49 with gcc 4.7");
#else
boost::property_tree::ptree pt;
boost::property_tree::read_json (ss, pt);
std::string method = pt.get<std::string>(I2P_CONTROL_PROPERTY_METHOD);
auto it = m_MethodHandlers.find (method);
if (it != m_MethodHandlers.end ())
{
std::map<std::string, std::string> params;
for (auto& v: pt.get_child (I2P_CONTROL_PROPERTY_PARAMS))
{
LogPrint (eLogInfo, v.first);
if (!v.first.empty())
params[v.first] = v.second.data ();
}
std::map<std::string, std::string> results;
(this->*(it->second))(params, results);
SendResponse (socket, buf, pt.get<std::string>(I2P_CONTROL_PROPERTY_ID), results, isHtml);
}
else
LogPrint (eLogWarning, "Unknown I2PControl method ", method);
#endif
}
catch (std::exception& ex)
{
LogPrint (eLogError, "I2PControl handle request: ", ex.what ());
}
catch (...)
{
LogPrint (eLogError, "I2PControl handle request unknown exception");
}
}
}
void I2PControlService::SendResponse (std::shared_ptr<boost::asio::ip::tcp::socket> socket,
std::shared_ptr<I2PControlBuffer> buf, const std::string& id,
const std::map<std::string, std::string>& results, bool isHtml)
{
boost::property_tree::ptree ptr;
for (auto& result: results)
ptr.put (boost::property_tree::ptree::path_type (result.first, '/'), result.second);
boost::property_tree::ptree pt;
pt.put (I2P_CONTROL_PROPERTY_ID, id);
pt.put_child (I2P_CONTROL_PROPERTY_RESULT, ptr);
pt.put ("jsonrpc", "2.0");
std::ostringstream ss;
#if GCC47_BOOST149
LogPrint (eLogError, "json_write is not supported due bug in boost 1.49 with gcc 4.7");
#else
boost::property_tree::write_json (ss, pt, false);
#endif
size_t len = ss.str ().length (), offset = 0;
if (isHtml)
{
std::ostringstream header;
header << "HTTP/1.1 200 OK\r\n";
header << "Connection: close\r\n";
header << "Content-Length: " << boost::lexical_cast<std::string>(len) << "\r\n";
header << "Content-Type: application/json\r\n";
header << "Date: ";
auto facet = new boost::local_time::local_time_facet ("%a, %d %b %Y %H:%M:%S GMT");
header.imbue(std::locale (header.getloc(), facet));
header << boost::posix_time::second_clock::local_time() << "\r\n";
header << "\r\n";
offset = header.str ().size ();
memcpy (buf->data (), header.str ().c_str (), offset);
}
memcpy (buf->data () + offset, ss.str ().c_str (), len);
boost::asio::async_write (*socket, boost::asio::buffer (buf->data (), len),
boost::asio::transfer_all (),
std::bind(&I2PControlService::HandleResponseSent, this,
std::placeholders::_1, std::placeholders::_2, socket, buf));
}
void I2PControlService::HandleResponseSent (const boost::system::error_code& ecode, std::size_t bytes_transferred,
std::shared_ptr<boost::asio::ip::tcp::socket> socket, std::shared_ptr<I2PControlBuffer> buf)
{
if (ecode)
LogPrint (eLogError, "I2PControl write error: ", ecode.message ());
socket->close ();
}
// handlers
void I2PControlService::AuthenticateHandler (const std::map<std::string, std::string>& params, std::map<std::string, std::string>& results)
{
const std::string& api = params.at (I2P_CONTROL_PARAM_API);
const std::string& password = params.at (I2P_CONTROL_PARAM_PASSWORD);
LogPrint (eLogDebug, "I2PControl Authenticate API=", api, " Password=", password);
if (password != m_Password)
LogPrint (eLogError, "I2PControl Authenticate Invalid password ", password, " expected ", m_Password);
results[I2P_CONTROL_PARAM_API] = api;
results[I2P_CONTROL_PARAM_TOKEN] = boost::lexical_cast<std::string>(i2p::util::GetSecondsSinceEpoch ());
}
void I2PControlService::EchoHandler (const std::map<std::string, std::string>& params, std::map<std::string, std::string>& results)
{
const std::string& echo = params.at (I2P_CONTROL_PARAM_ECHO);
LogPrint (eLogDebug, "I2PControl Echo Echo=", echo);
results[I2P_CONTROL_PARAM_RESULT] = echo;
}
// I2PControl
void I2PControlService::I2PControlHandler (const std::map<std::string, std::string>& params, std::map<std::string, std::string>& results)
{
LogPrint (eLogDebug, "I2PControl I2PControl");
for (auto& it: params)
{
LogPrint (eLogDebug, it.first);
auto it1 = m_I2PControlHandlers.find (it.first);
if (it1 != m_I2PControlHandlers.end ())
(this->*(it1->second))(it.second);
else
LogPrint (eLogError, "I2PControl NetworkSetting unknown request ", it.first);
}
}
// RouterInfo
void I2PControlService::RouterInfoHandler (const std::map<std::string, std::string>& params, std::map<std::string, std::string>& results)
{
LogPrint (eLogDebug, "I2PControl RouterInfo");
for (auto& it: params)
{
LogPrint (eLogDebug, it.first);
auto it1 = m_RouterInfoHandlers.find (it.first);
if (it1 != m_RouterInfoHandlers.end ())
(this->*(it1->second))(results);
else
LogPrint (eLogError, "I2PControl RouterInfo unknown request ", it.first);
}
}
void I2PControlService::NetDbKnownPeersHandler (std::map<std::string, std::string>& results)
{
results[I2P_CONTROL_ROUTER_INFO_NETDB_KNOWNPEERS] = boost::lexical_cast<std::string>(i2p::data::netdb.GetNumRouters ());
}
void I2PControlService::NetDbActivePeersHandler (std::map<std::string, std::string>& results)
{
results[I2P_CONTROL_ROUTER_INFO_NETDB_ACTIVEPEERS] = boost::lexical_cast<std::string>(i2p::transport::transports.GetPeers ().size ());
}
void I2PControlService::TunnelsParticipatingHandler (std::map<std::string, std::string>& results)
{
results[I2P_CONTROL_ROUTER_INFO_TUNNELS_PARTICIPATING] = boost::lexical_cast<std::string>(i2p::tunnel::tunnels.GetTransitTunnels ().size ());;
}
// RouterManager
void I2PControlService::RouterManagerHandler (const std::map<std::string, std::string>& params, std::map<std::string, std::string>& results)
{
LogPrint (eLogDebug, "I2PControl RouterManager");
for (auto& it: params)
{
LogPrint (eLogDebug, it.first);
auto it1 = m_RouterManagerHandlers.find (it.first);
if (it1 != m_RouterManagerHandlers.end ())
(this->*(it1->second))(results);
else
LogPrint (eLogError, "I2PControl RouterManager unknown request ", it.first);
}
}
void I2PControlService::ShutdownHandler (std::map<std::string, std::string>& results)
{
LogPrint (eLogInfo, "Shutdown requested");
results[I2P_CONTROL_ROUTER_MANAGER_SHUTDOWN] = "";
m_ShutdownTimer.expires_from_now (boost::posix_time::seconds(1)); // 1 second to make sure response has been sent
m_ShutdownTimer.async_wait (
[](const boost::system::error_code& ecode)
{
Daemon.running = 0;
});
}
void I2PControlService::ShutdownGracefulHandler (std::map<std::string, std::string>& results)
{
i2p::context.SetAcceptsTunnels (false);
int timeout = i2p::tunnel::tunnels.GetTransitTunnelsExpirationTimeout ();
LogPrint (eLogInfo, "Graceful shutdown requested. Will shutdown after ", timeout, " seconds");
results[I2P_CONTROL_ROUTER_MANAGER_SHUTDOWN_GRACEFUL] = "";
m_ShutdownTimer.expires_from_now (boost::posix_time::seconds(timeout + 1)); // + 1 second
m_ShutdownTimer.async_wait (
[](const boost::system::error_code& ecode)
{
Daemon.running = 0;
});
}
void I2PControlService::ReseedHandler (std::map<std::string, std::string>& results)
{
LogPrint (eLogInfo, "Reseed requested");
results[I2P_CONTROL_ROUTER_MANAGER_SHUTDOWN] = "";
i2p::data::netdb.Reseed ();
}
// network setting
void I2PControlService::NetworkSettingHandler (const std::map<std::string, std::string>& params, std::map<std::string, std::string>& results)
{
LogPrint (eLogDebug, "I2PControl NetworkSetting");
for (auto& it: params)
{
LogPrint (eLogDebug, it.first);
auto it1 = m_NetworkSettingHandlers.find (it.first);
if (it1 != m_NetworkSettingHandlers.end ())
(this->*(it1->second))(it.second, results);
else
LogPrint (eLogError, "I2PControl NetworkSetting unknown request ", it.first);
}
}
}
}

130
I2PControl.h Normal file
View File

@@ -0,0 +1,130 @@
#ifndef I2P_CONTROL_H__
#define I2P_CONTROL_H__
#include <inttypes.h>
#include <thread>
#include <memory>
#include <array>
#include <string>
#include <map>
#include <boost/asio.hpp>
namespace i2p
{
namespace client
{
const size_t I2P_CONTROL_MAX_REQUEST_SIZE = 1024;
typedef std::array<char, I2P_CONTROL_MAX_REQUEST_SIZE> I2PControlBuffer;
const char I2P_CONTROL_DEFAULT_PASSWORD[] = "itoopie";
const char I2P_CONTROL_PROPERTY_ID[] = "id";
const char I2P_CONTROL_PROPERTY_METHOD[] = "method";
const char I2P_CONTROL_PROPERTY_PARAMS[] = "params";
const char I2P_CONTROL_PROPERTY_RESULT[] = "result";
// methods
const char I2P_CONTROL_METHOD_AUTHENTICATE[] = "Authenticate";
const char I2P_CONTROL_METHOD_ECHO[] = "Echo";
const char I2P_CONTROL_METHOD_I2PCONTROL[] = "I2PControl";
const char I2P_CONTROL_METHOD_ROUTER_INFO[] = "RouterInfo";
const char I2P_CONTROL_METHOD_ROUTER_MANAGER[] = "RouterManager";
const char I2P_CONTROL_METHOD_NETWORK_SETTING[] = "NetworkSetting";
// params
const char I2P_CONTROL_PARAM_API[] = "API";
const char I2P_CONTROL_PARAM_PASSWORD[] = "Password";
const char I2P_CONTROL_PARAM_TOKEN[] = "Token";
const char I2P_CONTROL_PARAM_ECHO[] = "Echo";
const char I2P_CONTROL_PARAM_RESULT[] = "Result";
// I2PControl
const char I2P_CONTROL_I2PCONTROL_ADDRESS[] = "i2pcontrol.address";
const char I2P_CONTROL_I2PCONTROL_PASSWORD[] = "i2pcontrol.password";
const char I2P_CONTROL_I2PCONTROL_PORT[] = "i2pcontrol.port";
// RouterInfo requests
const char I2P_CONTROL_ROUTER_INFO_NETDB_KNOWNPEERS[] = "i2p.router.netdb.knownpeers";
const char I2P_CONTROL_ROUTER_INFO_NETDB_ACTIVEPEERS[] = "i2p.router.netdb.activepeers";
const char I2P_CONTROL_ROUTER_INFO_TUNNELS_PARTICIPATING[] = "i2p.router.net.tunnels.participating";
// RouterManager requests
const char I2P_CONTROL_ROUTER_MANAGER_SHUTDOWN[] = "Shutdown";
const char I2P_CONTROL_ROUTER_MANAGER_SHUTDOWN_GRACEFUL[] = "ShutdownGraceful";
const char I2P_CONTROL_ROUTER_MANAGER_RESEED[] = "Reseed";
class I2PControlService
{
public:
I2PControlService (int port);
~I2PControlService ();
void Start ();
void Stop ();
private:
void Run ();
void Accept ();
void HandleAccept(const boost::system::error_code& ecode, std::shared_ptr<boost::asio::ip::tcp::socket> socket);
void ReadRequest (std::shared_ptr<boost::asio::ip::tcp::socket> socket);
void HandleRequestReceived (const boost::system::error_code& ecode, size_t bytes_transferred,
std::shared_ptr<boost::asio::ip::tcp::socket> socket, std::shared_ptr<I2PControlBuffer> buf);
void SendResponse (std::shared_ptr<boost::asio::ip::tcp::socket> socket,
std::shared_ptr<I2PControlBuffer> buf, const std::string& id,
const std::map<std::string, std::string>& results, bool isHtml);
void HandleResponseSent (const boost::system::error_code& ecode, std::size_t bytes_transferred,
std::shared_ptr<boost::asio::ip::tcp::socket> socket, std::shared_ptr<I2PControlBuffer> buf);
private:
// methods
typedef void (I2PControlService::*MethodHandler)(const std::map<std::string, std::string>& params, std::map<std::string, std::string>& results);
void AuthenticateHandler (const std::map<std::string, std::string>& params, std::map<std::string, std::string>& results);
void EchoHandler (const std::map<std::string, std::string>& params, std::map<std::string, std::string>& results);
void I2PControlHandler (const std::map<std::string, std::string>& params, std::map<std::string, std::string>& results);
void RouterInfoHandler (const std::map<std::string, std::string>& params, std::map<std::string, std::string>& results);
void RouterManagerHandler (const std::map<std::string, std::string>& params, std::map<std::string, std::string>& results);
void NetworkSettingHandler (const std::map<std::string, std::string>& params, std::map<std::string, std::string>& results);
// I2PControl
typedef void (I2PControlService::*I2PControlRequestHandler)(const std::string& value);
// RouterInfo
typedef void (I2PControlService::*RouterInfoRequestHandler)(std::map<std::string, std::string>& results);
void NetDbKnownPeersHandler (std::map<std::string, std::string>& results);
void NetDbActivePeersHandler (std::map<std::string, std::string>& results);
void TunnelsParticipatingHandler (std::map<std::string, std::string>& results);
// RouterManager
typedef void (I2PControlService::*RouterManagerRequestHandler)(std::map<std::string, std::string>& results);
void ShutdownHandler (std::map<std::string, std::string>& results);
void ShutdownGracefulHandler (std::map<std::string, std::string>& results);
void ReseedHandler (std::map<std::string, std::string>& results);
// NetworkSetting
typedef void (I2PControlService::*NetworkSettingRequestHandler)(const std::string& value, std::map<std::string, std::string>& results);
private:
std::string m_Password;
bool m_IsRunning;
std::thread * m_Thread;
boost::asio::io_service m_Service;
boost::asio::ip::tcp::acceptor m_Acceptor;
boost::asio::deadline_timer m_ShutdownTimer;
std::map<std::string, MethodHandler> m_MethodHandlers;
std::map<std::string, I2PControlRequestHandler> m_I2PControlHandlers;
std::map<std::string, RouterInfoRequestHandler> m_RouterInfoHandlers;
std::map<std::string, RouterManagerRequestHandler> m_RouterManagerHandlers;
std::map<std::string, NetworkSettingRequestHandler> m_NetworkSettingHandlers;
};
}
}
#endif

80
I2PService.cpp Normal file
View File

@@ -0,0 +1,80 @@
#include "Destination.h"
#include "Identity.h"
#include "ClientContext.h"
#include "I2PService.h"
namespace i2p
{
namespace client
{
static const i2p::data::SigningKeyType I2P_SERVICE_DEFAULT_KEY_TYPE = i2p::data::SIGNING_KEY_TYPE_ECDSA_SHA256_P256;
I2PService::I2PService (ClientDestination * localDestination):
m_LocalDestination (localDestination ? localDestination :
i2p::client::context.CreateNewLocalDestination (false, I2P_SERVICE_DEFAULT_KEY_TYPE))
{
}
I2PService::I2PService (i2p::data::SigningKeyType kt):
m_LocalDestination (i2p::client::context.CreateNewLocalDestination (false, kt))
{
}
void I2PService::CreateStream (StreamRequestComplete streamRequestComplete, const std::string& dest, int port) {
assert(streamRequestComplete);
i2p::data::IdentHash identHash;
if (i2p::client::context.GetAddressBook ().GetIdentHash (dest, identHash))
m_LocalDestination->CreateStream (streamRequestComplete, identHash, port);
else
{
LogPrint (eLogWarning, "Remote destination ", dest, " not found");
streamRequestComplete (nullptr);
}
}
void TCPIPAcceptor::Start ()
{
m_Acceptor.listen ();
Accept ();
}
void TCPIPAcceptor::Stop ()
{
m_Acceptor.close();
m_Timer.cancel ();
ClearHandlers();
}
void TCPIPAcceptor::Accept ()
{
auto newSocket = new boost::asio::ip::tcp::socket (GetService ());
m_Acceptor.async_accept (*newSocket, std::bind (&TCPIPAcceptor::HandleAccept, this,
std::placeholders::_1, newSocket));
}
void TCPIPAcceptor::HandleAccept (const boost::system::error_code& ecode, boost::asio::ip::tcp::socket * socket)
{
if (!ecode)
{
LogPrint(eLogDebug,"--- ",GetName()," accepted");
auto handler = CreateHandler(socket);
if (handler) {
AddHandler(handler);
handler->Handle();
} else {
socket->close();
delete socket;
}
Accept();
}
else
{
if (ecode != boost::asio::error::operation_aborted)
LogPrint (eLogError,"--- ",GetName()," Closing socket on accept because: ", ecode.message ());
delete socket;
}
}
}
}

109
I2PService.h Normal file
View File

@@ -0,0 +1,109 @@
#ifndef I2PSERVICE_H__
#define I2PSERVICE_H__
#include <atomic>
#include <mutex>
#include <unordered_set>
#include <memory>
#include <boost/asio.hpp>
#include "Destination.h"
#include "Identity.h"
namespace i2p
{
namespace client
{
class I2PServiceHandler;
class I2PService
{
public:
I2PService (ClientDestination * localDestination = nullptr);
I2PService (i2p::data::SigningKeyType kt);
virtual ~I2PService () { ClearHandlers (); }
inline void AddHandler (std::shared_ptr<I2PServiceHandler> conn)
{
std::unique_lock<std::mutex> l(m_HandlersMutex);
m_Handlers.insert(conn);
}
inline void RemoveHandler (std::shared_ptr<I2PServiceHandler> conn)
{
std::unique_lock<std::mutex> l(m_HandlersMutex);
m_Handlers.erase(conn);
}
inline void ClearHandlers ()
{
std::unique_lock<std::mutex> l(m_HandlersMutex);
m_Handlers.clear();
}
inline ClientDestination * GetLocalDestination () { return m_LocalDestination; }
inline void SetLocalDestination (ClientDestination * dest) { m_LocalDestination = dest; }
void CreateStream (StreamRequestComplete streamRequestComplete, const std::string& dest, int port = 0);
inline boost::asio::io_service& GetService () { return m_LocalDestination->GetService (); }
virtual void Start () = 0;
virtual void Stop () = 0;
virtual const char* GetName() { return "Generic I2P Service"; }
private:
ClientDestination * m_LocalDestination;
std::unordered_set<std::shared_ptr<I2PServiceHandler> > m_Handlers;
std::mutex m_HandlersMutex;
};
/*Simple interface for I2PHandlers, allows detection of finalization amongst other things */
class I2PServiceHandler
{
public:
I2PServiceHandler(I2PService * parent) : m_Service(parent), m_Dead(false) { }
virtual ~I2PServiceHandler() { }
//If you override this make sure you call it from the children
virtual void Handle() {}; //Start handling the socket
protected:
// Call when terminating or handing over to avoid race conditions
inline bool Kill () { return m_Dead.exchange(true); }
// Call to know if the handler is dead
inline bool Dead () { return m_Dead; }
// Call when done to clean up (make sure Kill is called first)
inline void Done (std::shared_ptr<I2PServiceHandler> me) { if(m_Service) m_Service->RemoveHandler(me); }
// Call to talk with the owner
inline I2PService * GetOwner() { return m_Service; }
private:
I2PService *m_Service;
std::atomic<bool> m_Dead; //To avoid cleaning up multiple times
};
/* TODO: support IPv6 too */
//This is a service that listens for connections on the IP network and interacts with I2P
class TCPIPAcceptor: public I2PService
{
public:
TCPIPAcceptor (int port, ClientDestination * localDestination = nullptr) :
I2PService(localDestination),
m_Acceptor (GetService (), boost::asio::ip::tcp::endpoint (boost::asio::ip::tcp::v4(), port)),
m_Timer (GetService ()) {}
TCPIPAcceptor (int port, i2p::data::SigningKeyType kt) :
I2PService(kt),
m_Acceptor (GetService (), boost::asio::ip::tcp::endpoint (boost::asio::ip::tcp::v4(), port)),
m_Timer (GetService ()) {}
virtual ~TCPIPAcceptor () { TCPIPAcceptor::Stop(); }
//If you override this make sure you call it from the children
void Start ();
//If you override this make sure you call it from the children
void Stop ();
protected:
virtual std::shared_ptr<I2PServiceHandler> CreateHandler(boost::asio::ip::tcp::socket * socket) = 0;
virtual const char* GetName() { return "Generic TCP/IP accepting daemon"; }
private:
void Accept();
void HandleAccept(const boost::system::error_code& ecode, boost::asio::ip::tcp::socket * socket);
boost::asio::ip::tcp::acceptor m_Acceptor;
boost::asio::deadline_timer m_Timer;
};
}
}
#endif

View File

@@ -1,3 +1,4 @@
#include <cassert>
#include "base64.h"
#include "Log.h"
#include "Destination.h"
@@ -8,24 +9,25 @@ namespace i2p
{
namespace client
{
I2PTunnelConnection::I2PTunnelConnection (I2PTunnel * owner,
boost::asio::ip::tcp::socket * socket, const i2p::data::LeaseSet * leaseSet):
m_Socket (socket), m_Owner (owner), m_RemoteEndpoint (socket->remote_endpoint ()),
I2PTunnelConnection::I2PTunnelConnection (I2PService * owner,
boost::asio::ip::tcp::socket * socket, std::shared_ptr<const i2p::data::LeaseSet> leaseSet):
I2PServiceHandler(owner), m_Socket (socket), m_RemoteEndpoint (socket->remote_endpoint ()),
m_IsQuiet (true)
{
m_Stream = m_Owner->GetLocalDestination ()->CreateStream (*leaseSet);
m_Stream = GetOwner()->GetLocalDestination ()->CreateStream (leaseSet);
}
I2PTunnelConnection::I2PTunnelConnection (I2PTunnel * owner,
I2PTunnelConnection::I2PTunnelConnection (I2PService * owner,
boost::asio::ip::tcp::socket * socket, std::shared_ptr<i2p::stream::Stream> stream):
m_Socket (socket), m_Stream (stream), m_Owner (owner),
I2PServiceHandler(owner), m_Socket (socket), m_Stream (stream),
m_RemoteEndpoint (socket->remote_endpoint ()), m_IsQuiet (true)
{
}
I2PTunnelConnection::I2PTunnelConnection (I2PTunnel * owner, std::shared_ptr<i2p::stream::Stream> stream,
I2PTunnelConnection::I2PTunnelConnection (I2PService * owner, std::shared_ptr<i2p::stream::Stream> stream,
boost::asio::ip::tcp::socket * socket, const boost::asio::ip::tcp::endpoint& target, bool quiet):
m_Socket (socket), m_Stream (stream), m_Owner (owner), m_RemoteEndpoint (target), m_IsQuiet (quiet)
I2PServiceHandler(owner), m_Socket (socket), m_Stream (stream),
m_RemoteEndpoint (target), m_IsQuiet (quiet)
{
}
@@ -36,10 +38,13 @@ namespace client
void I2PTunnelConnection::I2PConnect (const uint8_t * msg, size_t len)
{
if (msg)
m_Stream->Send (msg, len); // connect and send
else
m_Stream->Send (m_Buffer, 0); // connect
if (m_Stream)
{
if (msg)
m_Stream->Send (msg, len); // connect and send
else
m_Stream->Send (m_Buffer, 0); // connect
}
StreamReceive ();
Receive ();
}
@@ -52,15 +57,15 @@ namespace client
}
void I2PTunnelConnection::Terminate ()
{
{
if (Kill()) return;
if (m_Stream)
{
m_Stream->Close ();
m_Stream.reset ();
}
m_Socket->close ();
if (m_Owner)
m_Owner->RemoveConnection (shared_from_this ());
Done(shared_from_this ());
}
void I2PTunnelConnection::Receive ()
@@ -146,50 +151,70 @@ namespace client
}
}
I2PTunnel::I2PTunnel (ClientDestination * localDestination) :
m_LocalDestination (localDestination ? localDestination :
i2p::client::context.CreateNewLocalDestination (false, I2P_TUNNEL_DEFAULT_KEY_TYPE))
/* This handler tries to stablish a connection with the desired server and dies if it fails to do so */
class I2PClientTunnelHandler: public I2PServiceHandler, public std::enable_shared_from_this<I2PClientTunnelHandler>
{
}
void I2PTunnel::AddConnection (std::shared_ptr<I2PTunnelConnection> conn)
{
m_Connections.insert (conn);
}
void I2PTunnel::RemoveConnection (std::shared_ptr<I2PTunnelConnection> conn)
{
m_Connections.erase (conn);
}
void I2PTunnel::ClearConnections ()
{
m_Connections.clear ();
}
I2PClientTunnel::I2PClientTunnel (const std::string& destination, int port, ClientDestination * localDestination):
I2PTunnel (localDestination),
m_Acceptor (GetService (), boost::asio::ip::tcp::endpoint (boost::asio::ip::tcp::v4(), port)),
m_Timer (GetService ()), m_Destination (destination), m_DestinationIdentHash (nullptr)
{
}
public:
I2PClientTunnelHandler (I2PClientTunnel * parent, i2p::data::IdentHash destination,
boost::asio::ip::tcp::socket * socket):
I2PServiceHandler(parent), m_DestinationIdentHash(destination), m_Socket(socket) {}
void Handle();
void Terminate();
private:
void HandleStreamRequestComplete (std::shared_ptr<i2p::stream::Stream> stream);
i2p::data::IdentHash m_DestinationIdentHash;
boost::asio::ip::tcp::socket * m_Socket;
};
I2PClientTunnel::~I2PClientTunnel ()
void I2PClientTunnelHandler::Handle()
{
Stop ();
GetOwner()->GetLocalDestination ()->CreateStream (std::bind (&I2PClientTunnelHandler::HandleStreamRequestComplete,
shared_from_this(), std::placeholders::_1), m_DestinationIdentHash);
}
void I2PClientTunnelHandler::HandleStreamRequestComplete (std::shared_ptr<i2p::stream::Stream> stream)
{
if (stream)
{
if (Kill()) return;
LogPrint (eLogInfo,"New I2PTunnel connection");
auto connection = std::make_shared<I2PTunnelConnection>(GetOwner(), m_Socket, stream);
GetOwner()->AddHandler (connection);
connection->I2PConnect ();
Done(shared_from_this());
}
else
{
LogPrint (eLogError,"I2P Client Tunnel Issue when creating the stream, check the previous warnings for more info.");
Terminate();
}
}
void I2PClientTunnelHandler::Terminate()
{
if (Kill()) return;
if (m_Socket)
{
m_Socket->close();
delete m_Socket;
m_Socket = nullptr;
}
Done(shared_from_this());
}
I2PClientTunnel::I2PClientTunnel (const std::string& destination, int port, ClientDestination * localDestination):
TCPIPAcceptor (port,localDestination), m_Destination (destination), m_DestinationIdentHash (nullptr)
{}
void I2PClientTunnel::Start ()
{
TCPIPAcceptor::Start ();
GetIdentHash();
m_Acceptor.listen ();
Accept ();
}
void I2PClientTunnel::Stop ()
{
m_Acceptor.close();
m_Timer.cancel ();
ClearConnections ();
TCPIPAcceptor::Stop();
auto *originalIdentHash = m_DestinationIdentHash;
m_DestinationIdentHash = nullptr;
delete originalIdentHash;
@@ -209,55 +234,17 @@ namespace client
return m_DestinationIdentHash;
}
void I2PClientTunnel::Accept ()
std::shared_ptr<I2PServiceHandler> I2PClientTunnel::CreateHandler(boost::asio::ip::tcp::socket * socket)
{
auto newSocket = new boost::asio::ip::tcp::socket (GetService ());
m_Acceptor.async_accept (*newSocket, std::bind (&I2PClientTunnel::HandleAccept, this,
std::placeholders::_1, newSocket));
}
void I2PClientTunnel::HandleAccept (const boost::system::error_code& ecode, boost::asio::ip::tcp::socket * socket)
{
if (!ecode)
{
const i2p::data::IdentHash *identHash = GetIdentHash();
if (identHash)
GetLocalDestination ()->CreateStream (
std::bind (&I2PClientTunnel::HandleStreamRequestComplete,
this, std::placeholders::_1, socket), *identHash);
else
{
LogPrint (eLogError,"Closing socket");
delete socket;
}
Accept ();
}
const i2p::data::IdentHash *identHash = GetIdentHash();
if (identHash)
return std::make_shared<I2PClientTunnelHandler>(this, *identHash, socket);
else
{
LogPrint (eLogError,"Closing socket on accept because: ", ecode.message ());
delete socket;
}
}
void I2PClientTunnel::HandleStreamRequestComplete (std::shared_ptr<i2p::stream::Stream> stream, boost::asio::ip::tcp::socket * socket)
{
if (stream)
{
LogPrint (eLogInfo,"New I2PTunnel connection");
auto connection = std::make_shared<I2PTunnelConnection>(this, socket, stream);
AddConnection (connection);
connection->I2PConnect ();
}
else
{
LogPrint (eLogError,"Issue when creating the stream, check the previous warnings for more info.");
delete socket;
}
return nullptr;
}
I2PServerTunnel::I2PServerTunnel (const std::string& address, int port, ClientDestination * localDestination):
I2PTunnel (localDestination), m_Endpoint (boost::asio::ip::address::from_string (address), port)
I2PService (localDestination), m_Endpoint (boost::asio::ip::address::from_string (address), port)
{
}
@@ -268,7 +255,7 @@ namespace client
void I2PServerTunnel::Stop ()
{
ClearConnections ();
ClearHandlers ();
}
void I2PServerTunnel::Accept ()
@@ -285,7 +272,7 @@ namespace client
if (stream)
{
auto conn = std::make_shared<I2PTunnelConnection> (this, stream, new boost::asio::ip::tcp::socket (GetService ()), m_Endpoint);
AddConnection (conn);
AddHandler (conn);
conn->Connect ();
}
}

View File

@@ -9,6 +9,7 @@
#include "Identity.h"
#include "Destination.h"
#include "Streaming.h"
#include "I2PService.h"
namespace i2p
{
@@ -17,21 +18,19 @@ namespace client
const size_t I2P_TUNNEL_CONNECTION_BUFFER_SIZE = 8192;
const int I2P_TUNNEL_CONNECTION_MAX_IDLE = 3600; // in seconds
const int I2P_TUNNEL_DESTINATION_REQUEST_TIMEOUT = 10; // in seconds
const i2p::data::SigningKeyType I2P_TUNNEL_DEFAULT_KEY_TYPE = i2p::data::SIGNING_KEY_TYPE_ECDSA_SHA256_P256;
class I2PTunnel;
class I2PTunnelConnection: public std::enable_shared_from_this<I2PTunnelConnection>
class I2PTunnelConnection: public I2PServiceHandler, public std::enable_shared_from_this<I2PTunnelConnection>
{
public:
I2PTunnelConnection (I2PTunnel * owner, boost::asio::ip::tcp::socket * socket,
const i2p::data::LeaseSet * leaseSet); // to I2P
I2PTunnelConnection (I2PTunnel * owner, boost::asio::ip::tcp::socket * socket,
I2PTunnelConnection (I2PService * owner, boost::asio::ip::tcp::socket * socket,
std::shared_ptr<const i2p::data::LeaseSet> leaseSet); // to I2P
I2PTunnelConnection (I2PService * owner, boost::asio::ip::tcp::socket * socket,
std::shared_ptr<i2p::stream::Stream> stream); // to I2P using simplified API :)
I2PTunnelConnection (I2PTunnel * owner, std::shared_ptr<i2p::stream::Stream> stream, boost::asio::ip::tcp::socket * socket,
I2PTunnelConnection (I2PService * owner, std::shared_ptr<i2p::stream::Stream> stream, boost::asio::ip::tcp::socket * socket,
const boost::asio::ip::tcp::endpoint& target, bool quiet = true); // from I2P
~I2PTunnelConnection ();
void I2PConnect (const uint8_t * msg = nullptr, size_t len = 0);
void Connect ();
@@ -52,38 +51,22 @@ namespace client
uint8_t m_Buffer[I2P_TUNNEL_CONNECTION_BUFFER_SIZE], m_StreamBuffer[I2P_TUNNEL_CONNECTION_BUFFER_SIZE];
boost::asio::ip::tcp::socket * m_Socket;
std::shared_ptr<i2p::stream::Stream> m_Stream;
I2PTunnel * m_Owner;
boost::asio::ip::tcp::endpoint m_RemoteEndpoint;
bool m_IsQuiet; // don't send destination
};
};
class I2PTunnel
class I2PClientTunnel: public TCPIPAcceptor
{
public:
protected:
I2PTunnel (ClientDestination * localDestination = nullptr);
virtual ~I2PTunnel () { ClearConnections (); };
// Implements TCPIPAcceptor
std::shared_ptr<I2PServiceHandler> CreateHandler(boost::asio::ip::tcp::socket * socket);
const char* GetName() { return "I2P Client Tunnel"; }
void AddConnection (std::shared_ptr<I2PTunnelConnection> conn);
void RemoveConnection (std::shared_ptr<I2PTunnelConnection> conn);
void ClearConnections ();
ClientDestination * GetLocalDestination () { return m_LocalDestination; };
void SetLocalDestination (ClientDestination * dest) { m_LocalDestination = dest; };
boost::asio::io_service& GetService () { return m_LocalDestination->GetService (); };
private:
ClientDestination * m_LocalDestination;
std::set<std::shared_ptr<I2PTunnelConnection> > m_Connections;
};
class I2PClientTunnel: public I2PTunnel
{
public:
I2PClientTunnel (const std::string& destination, int port, ClientDestination * localDestination = nullptr);
~I2PClientTunnel ();
~I2PClientTunnel () {}
void Start ();
void Stop ();
@@ -91,19 +74,12 @@ namespace client
private:
const i2p::data::IdentHash * GetIdentHash ();
void Accept ();
void HandleAccept (const boost::system::error_code& ecode, boost::asio::ip::tcp::socket * socket);
void HandleStreamRequestComplete (std::shared_ptr<i2p::stream::Stream> stream, boost::asio::ip::tcp::socket * socket);
private:
boost::asio::ip::tcp::acceptor m_Acceptor;
boost::asio::deadline_timer m_Timer;
std::string m_Destination;
const i2p::data::IdentHash * m_DestinationIdentHash;
};
class I2PServerTunnel: public I2PTunnel
class I2PServerTunnel: public I2PService
{
public:
@@ -121,7 +97,7 @@ namespace client
boost::asio::ip::tcp::endpoint m_Endpoint;
};
}
}
}
#endif

354
LICENSE
View File

@@ -1,339 +1,27 @@
GNU GENERAL PUBLIC LICENSE
Version 2, June 1991
Copyright (c) 2013-2015, The PurpleI2P Project
Copyright (C) 1989, 1991 Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
Everyone is permitted to copy and distribute verbatim copies
of this license document, but changing it is not allowed.
All rights reserved.
Preamble
Redistribution and use in source and binary forms, with or without modification, are
permitted provided that the following conditions are met:
The licenses for most software are designed to take away your
freedom to share and change it. By contrast, the GNU General Public
License is intended to guarantee your freedom to share and change free
software--to make sure the software is free for all its users. This
General Public License applies to most of the Free Software
Foundation's software and to any other program whose authors commit to
using it. (Some other Free Software Foundation software is covered by
the GNU Lesser General Public License instead.) You can apply it to
your programs, too.
1. Redistributions of source code must retain the above copyright notice, this list of
conditions and the following disclaimer.
When we speak of free software, we are referring to freedom, not
price. Our General Public Licenses are designed to make sure that you
have the freedom to distribute copies of free software (and charge for
this service if you wish), that you receive source code or can get it
if you want it, that you can change the software or use pieces of it
in new free programs; and that you know you can do these things.
2. Redistributions in binary form must reproduce the above copyright notice, this list of
conditions and the following disclaimer in the documentation and/or other materials
provided with the distribution.
To protect your rights, we need to make restrictions that forbid
anyone to deny you these rights or to ask you to surrender the rights.
These restrictions translate to certain responsibilities for you if you
distribute copies of the software, or if you modify it.
3. Neither the name of the copyright holder nor the names of its contributors may be used
to endorse or promote products derived from this software without specific prior written
permission.
For example, if you distribute copies of such a program, whether
gratis or for a fee, you must give the recipients all the rights that
you have. You must make sure that they, too, receive or can get the
source code. And you must show them these terms so they know their
rights.
We protect your rights with two steps: (1) copyright the software, and
(2) offer you this license which gives you legal permission to copy,
distribute and/or modify the software.
Also, for each author's protection and ours, we want to make certain
that everyone understands that there is no warranty for this free
software. If the software is modified by someone else and passed on, we
want its recipients to know that what they have is not the original, so
that any problems introduced by others will not reflect on the original
authors' reputations.
Finally, any free program is threatened constantly by software
patents. We wish to avoid the danger that redistributors of a free
program will individually obtain patent licenses, in effect making the
program proprietary. To prevent this, we have made it clear that any
patent must be licensed for everyone's free use or not licensed at all.
The precise terms and conditions for copying, distribution and
modification follow.
GNU GENERAL PUBLIC LICENSE
TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
0. This License applies to any program or other work which contains
a notice placed by the copyright holder saying it may be distributed
under the terms of this General Public License. The "Program", below,
refers to any such program or work, and a "work based on the Program"
means either the Program or any derivative work under copyright law:
that is to say, a work containing the Program or a portion of it,
either verbatim or with modifications and/or translated into another
language. (Hereinafter, translation is included without limitation in
the term "modification".) Each licensee is addressed as "you".
Activities other than copying, distribution and modification are not
covered by this License; they are outside its scope. The act of
running the Program is not restricted, and the output from the Program
is covered only if its contents constitute a work based on the
Program (independent of having been made by running the Program).
Whether that is true depends on what the Program does.
1. You may copy and distribute verbatim copies of the Program's
source code as you receive it, in any medium, provided that you
conspicuously and appropriately publish on each copy an appropriate
copyright notice and disclaimer of warranty; keep intact all the
notices that refer to this License and to the absence of any warranty;
and give any other recipients of the Program a copy of this License
along with the Program.
You may charge a fee for the physical act of transferring a copy, and
you may at your option offer warranty protection in exchange for a fee.
2. You may modify your copy or copies of the Program or any portion
of it, thus forming a work based on the Program, and copy and
distribute such modifications or work under the terms of Section 1
above, provided that you also meet all of these conditions:
a) You must cause the modified files to carry prominent notices
stating that you changed the files and the date of any change.
b) You must cause any work that you distribute or publish, that in
whole or in part contains or is derived from the Program or any
part thereof, to be licensed as a whole at no charge to all third
parties under the terms of this License.
c) If the modified program normally reads commands interactively
when run, you must cause it, when started running for such
interactive use in the most ordinary way, to print or display an
announcement including an appropriate copyright notice and a
notice that there is no warranty (or else, saying that you provide
a warranty) and that users may redistribute the program under
these conditions, and telling the user how to view a copy of this
License. (Exception: if the Program itself is interactive but
does not normally print such an announcement, your work based on
the Program is not required to print an announcement.)
These requirements apply to the modified work as a whole. If
identifiable sections of that work are not derived from the Program,
and can be reasonably considered independent and separate works in
themselves, then this License, and its terms, do not apply to those
sections when you distribute them as separate works. But when you
distribute the same sections as part of a whole which is a work based
on the Program, the distribution of the whole must be on the terms of
this License, whose permissions for other licensees extend to the
entire whole, and thus to each and every part regardless of who wrote it.
Thus, it is not the intent of this section to claim rights or contest
your rights to work written entirely by you; rather, the intent is to
exercise the right to control the distribution of derivative or
collective works based on the Program.
In addition, mere aggregation of another work not based on the Program
with the Program (or with a work based on the Program) on a volume of
a storage or distribution medium does not bring the other work under
the scope of this License.
3. You may copy and distribute the Program (or a work based on it,
under Section 2) in object code or executable form under the terms of
Sections 1 and 2 above provided that you also do one of the following:
a) Accompany it with the complete corresponding machine-readable
source code, which must be distributed under the terms of Sections
1 and 2 above on a medium customarily used for software interchange; or,
b) Accompany it with a written offer, valid for at least three
years, to give any third party, for a charge no more than your
cost of physically performing source distribution, a complete
machine-readable copy of the corresponding source code, to be
distributed under the terms of Sections 1 and 2 above on a medium
customarily used for software interchange; or,
c) Accompany it with the information you received as to the offer
to distribute corresponding source code. (This alternative is
allowed only for noncommercial distribution and only if you
received the program in object code or executable form with such
an offer, in accord with Subsection b above.)
The source code for a work means the preferred form of the work for
making modifications to it. For an executable work, complete source
code means all the source code for all modules it contains, plus any
associated interface definition files, plus the scripts used to
control compilation and installation of the executable. However, as a
special exception, the source code distributed need not include
anything that is normally distributed (in either source or binary
form) with the major components (compiler, kernel, and so on) of the
operating system on which the executable runs, unless that component
itself accompanies the executable.
If distribution of executable or object code is made by offering
access to copy from a designated place, then offering equivalent
access to copy the source code from the same place counts as
distribution of the source code, even though third parties are not
compelled to copy the source along with the object code.
4. You may not copy, modify, sublicense, or distribute the Program
except as expressly provided under this License. Any attempt
otherwise to copy, modify, sublicense or distribute the Program is
void, and will automatically terminate your rights under this License.
However, parties who have received copies, or rights, from you under
this License will not have their licenses terminated so long as such
parties remain in full compliance.
5. You are not required to accept this License, since you have not
signed it. However, nothing else grants you permission to modify or
distribute the Program or its derivative works. These actions are
prohibited by law if you do not accept this License. Therefore, by
modifying or distributing the Program (or any work based on the
Program), you indicate your acceptance of this License to do so, and
all its terms and conditions for copying, distributing or modifying
the Program or works based on it.
6. Each time you redistribute the Program (or any work based on the
Program), the recipient automatically receives a license from the
original licensor to copy, distribute or modify the Program subject to
these terms and conditions. You may not impose any further
restrictions on the recipients' exercise of the rights granted herein.
You are not responsible for enforcing compliance by third parties to
this License.
7. If, as a consequence of a court judgment or allegation of patent
infringement or for any other reason (not limited to patent issues),
conditions are imposed on you (whether by court order, agreement or
otherwise) that contradict the conditions of this License, they do not
excuse you from the conditions of this License. If you cannot
distribute so as to satisfy simultaneously your obligations under this
License and any other pertinent obligations, then as a consequence you
may not distribute the Program at all. For example, if a patent
license would not permit royalty-free redistribution of the Program by
all those who receive copies directly or indirectly through you, then
the only way you could satisfy both it and this License would be to
refrain entirely from distribution of the Program.
If any portion of this section is held invalid or unenforceable under
any particular circumstance, the balance of the section is intended to
apply and the section as a whole is intended to apply in other
circumstances.
It is not the purpose of this section to induce you to infringe any
patents or other property right claims or to contest validity of any
such claims; this section has the sole purpose of protecting the
integrity of the free software distribution system, which is
implemented by public license practices. Many people have made
generous contributions to the wide range of software distributed
through that system in reliance on consistent application of that
system; it is up to the author/donor to decide if he or she is willing
to distribute software through any other system and a licensee cannot
impose that choice.
This section is intended to make thoroughly clear what is believed to
be a consequence of the rest of this License.
8. If the distribution and/or use of the Program is restricted in
certain countries either by patents or by copyrighted interfaces, the
original copyright holder who places the Program under this License
may add an explicit geographical distribution limitation excluding
those countries, so that distribution is permitted only in or among
countries not thus excluded. In such case, this License incorporates
the limitation as if written in the body of this License.
9. The Free Software Foundation may publish revised and/or new versions
of the General Public License from time to time. Such new versions will
be similar in spirit to the present version, but may differ in detail to
address new problems or concerns.
Each version is given a distinguishing version number. If the Program
specifies a version number of this License which applies to it and "any
later version", you have the option of following the terms and conditions
either of that version or of any later version published by the Free
Software Foundation. If the Program does not specify a version number of
this License, you may choose any version ever published by the Free Software
Foundation.
10. If you wish to incorporate parts of the Program into other free
programs whose distribution conditions are different, write to the author
to ask for permission. For software which is copyrighted by the Free
Software Foundation, write to the Free Software Foundation; we sometimes
make exceptions for this. Our decision will be guided by the two goals
of preserving the free status of all derivatives of our free software and
of promoting the sharing and reuse of software generally.
NO WARRANTY
11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY
FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN
OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES
PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED
OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS
TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE
PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING,
REPAIR OR CORRECTION.
12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR
REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES,
INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING
OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED
TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY
YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER
PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE
POSSIBILITY OF SUCH DAMAGES.
END OF TERMS AND CONDITIONS
How to Apply These Terms to Your New Programs
If you develop a new program, and you want it to be of the greatest
possible use to the public, the best way to achieve this is to make it
free software which everyone can redistribute and change under these terms.
To do so, attach the following notices to the program. It is safest
to attach them to the start of each source file to most effectively
convey the exclusion of warranty; and each file should have at least
the "copyright" line and a pointer to where the full notice is found.
i2p router for Linux written on C++
Copyright (C) 2013 orignal
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License along
with this program; if not, write to the Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
Also add information on how to contact you by electronic and paper mail.
If the program is interactive, make it output a short notice like this
when it starts in an interactive mode:
Gnomovision version 69, Copyright (C) year name of author
Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
This is free software, and you are welcome to redistribute it
under certain conditions; type `show c' for details.
The hypothetical commands `show w' and `show c' should show the appropriate
parts of the General Public License. Of course, the commands you use may
be called something other than `show w' and `show c'; they could even be
mouse-clicks or menu items--whatever suits your program.
You should also get your employer (if you work as a programmer) or your
school, if any, to sign a "copyright disclaimer" for the program, if
necessary. Here is a sample; alter the names:
Yoyodyne, Inc., hereby disclaims all copyright interest in the program
`Gnomovision' (which makes passes at compilers) written by James Hacker.
{signature of Ty Coon}, 1 April 1989
Ty Coon, President of Vice
This General Public License does not permit incorporating your program into
proprietary programs. If your program is a subroutine library, you may
consider it more useful to permit linking proprietary applications with the
library. If this is what you want to do, use the GNU Lesser General
Public License instead of this License.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY
EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR
TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

View File

@@ -25,7 +25,7 @@ endif
all: mk_build_dir $(SHLIB) $(I2PD)
mk_build_dir:
test -d obj || mkdir obj
mkdir -p obj
api: $(SHLIB)
@@ -37,12 +37,12 @@ api: $(SHLIB)
## custom FLAGS to work at build-time.
deps:
@test -d obj || mkdir obj
@mkdir -p obj
$(CXX) $(CXXFLAGS) $(NEEDED_CXXFLAGS) -MM *.cpp > $(DEPS)
@sed -i -e '/\.o:/ s/^/obj\//' $(DEPS)
obj/%.o : %.cpp
@test -d obj || mkdir obj
@mkdir -p obj
$(CXX) $(CXXFLAGS) $(NEEDED_CXXFLAGS) $(INCFLAGS) $(CPU_FLAGS) -c -o $@ $<
# '-' is 'ignore if missing' on first run

View File

@@ -1,4 +1,4 @@
CXXFLAGS = -g -Wall -fPIC
CXXFLAGS = -g -Wall
INCFLAGS =
## NOTE: The NEEDED_CXXFLAGS are here so that custom CXXFLAGS can be specified at build time
@@ -22,6 +22,8 @@ else # not supported
$(error Compiler too old)
endif
NEEDED_CXXFLAGS += -fPIC
ifeq ($(USE_STATIC),yes)
LIBDIR := /usr/lib
LDLIBS = $(LIBDIR)/libboost_system.a

View File

@@ -18,10 +18,11 @@ namespace i2p
{
namespace transport
{
NTCPSession::NTCPSession (boost::asio::io_service& service, std::shared_ptr<const i2p::data::RouterInfo> in_RemoteRouter):
TransportSession (in_RemoteRouter), m_Socket (service),
m_TerminationTimer (service), m_IsEstablished (false), m_ReceiveBufferOffset (0),
m_NextMessage (nullptr), m_NumSentBytes (0), m_NumReceivedBytes (0)
NTCPSession::NTCPSession (NTCPServer& server, std::shared_ptr<const i2p::data::RouterInfo> in_RemoteRouter):
TransportSession (in_RemoteRouter), m_Server (server), m_Socket (m_Server.GetService ()),
m_TerminationTimer (m_Server.GetService ()), m_IsEstablished (false), m_IsTerminated (false),
m_ReceiveBufferOffset (0), m_NextMessage (nullptr), m_IsSending (false),
m_NumSentBytes (0), m_NumReceivedBytes (0)
{
m_DHKeysPair = transports.GetNextDHKeysPair ();
m_Establisher = new Establisher;
@@ -30,11 +31,6 @@ namespace transport
NTCPSession::~NTCPSession ()
{
delete m_Establisher;
if (m_NextMessage)
i2p::DeleteI2NPMessage (m_NextMessage);
for (auto it :m_DelayedMessages)
i2p::DeleteI2NPMessage (it);
m_DelayedMessages.clear ();
}
void NTCPSession::CreateAESKey (uint8_t * pubKey, i2p::crypto::AESKey& key)
@@ -73,24 +69,31 @@ namespace transport
}
}
void NTCPSession::Done ()
{
m_Server.GetService ().post (std::bind (&NTCPSession::Terminate, shared_from_this ()));
}
void NTCPSession::Terminate ()
{
m_IsEstablished = false;
m_Socket.close ();
int numDelayed = 0;
for (auto it :m_DelayedMessages)
if (!m_IsTerminated)
{
// try to send them again
if (m_RemoteRouter)
transports.SendMessage (m_RemoteRouter->GetIdentHash (), it);
numDelayed++;
m_IsTerminated = true;
m_IsEstablished = false;
m_Socket.close ();
transports.PeerDisconnected (shared_from_this ());
m_Server.RemoveNTCPSession (shared_from_this ());
for (auto it: m_SendQueue)
DeleteI2NPMessage (it);
m_SendQueue.clear ();
if (m_NextMessage)
{
i2p::DeleteI2NPMessage (m_NextMessage);
m_NextMessage = nullptr;
}
m_TerminationTimer.cancel ();
LogPrint (eLogInfo, "NTCP session terminated");
}
m_DelayedMessages.clear ();
if (numDelayed > 0)
LogPrint (eLogWarning, "NTCP session ", numDelayed, " not sent");
// TODO: notify tunnels
transports.RemoveNTCPSession (shared_from_this ());
LogPrint ("NTCP session terminated");
}
void NTCPSession::Connected ()
@@ -104,14 +107,9 @@ namespace transport
m_DHKeysPair = nullptr;
SendTimeSyncMessage ();
SendI2NPMessage (CreateDatabaseStoreMsg ()); // we tell immediately who we are
PostI2NPMessage (CreateDatabaseStoreMsg ()); // we tell immediately who we are
if (!m_DelayedMessages.empty ())
{
for (auto it :m_DelayedMessages)
SendI2NPMessage (it);
m_DelayedMessages.clear ();
}
transports.PeerConnected (shared_from_this ());
}
void NTCPSession::ClientLogin ()
@@ -128,27 +126,34 @@ namespace transport
boost::asio::async_write (m_Socket, boost::asio::buffer (&m_Establisher->phase1, sizeof (NTCPPhase1)), boost::asio::transfer_all (),
std::bind(&NTCPSession::HandlePhase1Sent, shared_from_this (), std::placeholders::_1, std::placeholders::_2));
ScheduleTermination ();
}
void NTCPSession::ServerLogin ()
{
// receive Phase1
boost::asio::async_read (m_Socket, boost::asio::buffer(&m_Establisher->phase1, sizeof (NTCPPhase1)), boost::asio::transfer_all (),
std::bind(&NTCPSession::HandlePhase1Received, shared_from_this (),
std::placeholders::_1, std::placeholders::_2));
boost::system::error_code ec;
auto ep = m_Socket.remote_endpoint(ec);
if (!ec)
{
m_ConnectedFrom = ep.address ();
// receive Phase1
boost::asio::async_read (m_Socket, boost::asio::buffer(&m_Establisher->phase1, sizeof (NTCPPhase1)), boost::asio::transfer_all (),
std::bind(&NTCPSession::HandlePhase1Received, shared_from_this (),
std::placeholders::_1, std::placeholders::_2));
ScheduleTermination ();
}
}
void NTCPSession::HandlePhase1Sent (const boost::system::error_code& ecode, std::size_t bytes_transferred)
{
if (ecode)
{
LogPrint (eLogWarning, "Couldn't send Phase 1 message: ", ecode.message ());
LogPrint (eLogError, "Couldn't send Phase 1 message: ", ecode.message ());
if (ecode != boost::asio::error::operation_aborted)
Terminate ();
}
else
{
LogPrint (eLogDebug, "Phase 1 sent: ", bytes_transferred);
boost::asio::async_read (m_Socket, boost::asio::buffer(&m_Establisher->phase2, sizeof (NTCPPhase2)), boost::asio::transfer_all (),
std::bind(&NTCPSession::HandlePhase2Received, shared_from_this (),
std::placeholders::_1, std::placeholders::_2));
@@ -165,7 +170,6 @@ namespace transport
}
else
{
LogPrint (eLogDebug, "Phase 1 received: ", bytes_transferred);
// verify ident
uint8_t digest[32];
CryptoPP::SHA256().CalculateDigest(digest, m_Establisher->phase1.pubKey, 256);
@@ -215,13 +219,12 @@ namespace transport
{
if (ecode)
{
LogPrint (eLogWarning, "Couldn't send Phase 2 message: ", ecode.message ());
LogPrint (eLogError, "Couldn't send Phase 2 message: ", ecode.message ());
if (ecode != boost::asio::error::operation_aborted)
Terminate ();
}
else
{
LogPrint (eLogDebug, "Phase 2 sent: ", bytes_transferred);
boost::asio::async_read (m_Socket, boost::asio::buffer(m_ReceiveBuffer, NTCP_DEFAULT_PHASE3_SIZE), boost::asio::transfer_all (),
std::bind(&NTCPSession::HandlePhase3Received, shared_from_this (),
std::placeholders::_1, std::placeholders::_2, tsB));
@@ -232,7 +235,7 @@ namespace transport
{
if (ecode)
{
LogPrint ("Phase 2 read error: ", ecode.message (), ". Wrong ident assumed");
LogPrint (eLogError, "Phase 2 read error: ", ecode.message (), ". Wrong ident assumed");
if (ecode != boost::asio::error::operation_aborted)
{
// this RI is not valid
@@ -244,8 +247,6 @@ namespace transport
}
else
{
LogPrint (eLogDebug, "Phase 2 received: ", bytes_transferred);
i2p::crypto::AESKey aesKey;
CreateAESKey (m_Establisher->phase2.pubKey, aesKey);
m_Decryption.SetKey (aesKey);
@@ -308,13 +309,12 @@ namespace transport
{
if (ecode)
{
LogPrint (eLogWarning, "Couldn't send Phase 3 message: ", ecode.message ());
LogPrint (eLogError, "Couldn't send Phase 3 message: ", ecode.message ());
if (ecode != boost::asio::error::operation_aborted)
Terminate ();
}
else
{
LogPrint (eLogDebug, "Phase 3 sent: ", bytes_transferred);
// wait for phase4
auto signatureLen = m_RemoteIdentity.GetSignatureLen ();
size_t paddingSize = signatureLen & 0x0F; // %16
@@ -335,11 +335,15 @@ namespace transport
}
else
{
LogPrint (eLogDebug, "Phase 3 received: ", bytes_transferred);
m_Decryption.Decrypt (m_ReceiveBuffer, bytes_transferred, m_ReceiveBuffer);
uint8_t * buf = m_ReceiveBuffer;
uint16_t size = bufbe16toh (buf);
m_RemoteIdentity.FromBuffer (buf + 2, size);
if (m_Server.FindNTCPSession (m_RemoteIdentity.GetIdentHash ()))
{
LogPrint (eLogError, "NTCP session already exists");
Terminate ();
}
size_t expectedSize = size + 2/*size*/ + 4/*timestamp*/ + m_RemoteIdentity.GetSignatureLen ();
size_t paddingLen = expectedSize & 0x0F;
if (paddingLen) paddingLen = (16 - paddingLen);
@@ -347,7 +351,6 @@ namespace transport
{
// we need more bytes for Phase3
expectedSize += paddingLen;
LogPrint (eLogDebug, "Wait for ", expectedSize, " more bytes for Phase3");
boost::asio::async_read (m_Socket, boost::asio::buffer(m_ReceiveBuffer + NTCP_DEFAULT_PHASE3_SIZE, expectedSize), boost::asio::transfer_all (),
std::bind(&NTCPSession::HandlePhase3ExtraReceived, shared_from_this (),
std::placeholders::_1, std::placeholders::_2, tsB, paddingLen));
@@ -367,7 +370,6 @@ namespace transport
}
else
{
LogPrint (eLogDebug, "Phase 3 extra received: ", bytes_transferred);
m_Decryption.Decrypt (m_ReceiveBuffer + NTCP_DEFAULT_PHASE3_SIZE, bytes_transferred, m_ReceiveBuffer+ NTCP_DEFAULT_PHASE3_SIZE);
HandlePhase3 (tsB, paddingLen);
}
@@ -425,9 +427,8 @@ namespace transport
}
else
{
LogPrint (eLogDebug, "Phase 4 sent: ", bytes_transferred);
LogPrint ("NTCP server session connected");
transports.AddNTCPSession (shared_from_this ());
LogPrint (eLogInfo, "NTCP server session from ", m_Socket.remote_endpoint (), " connected");
m_Server.AddNTCPSession (shared_from_this ());
Connected ();
m_ReceiveBufferOffset = 0;
@@ -440,7 +441,7 @@ namespace transport
{
if (ecode)
{
LogPrint (eLogError, "Phase 4 read error: ", ecode.message ());
LogPrint (eLogError, "Phase 4 read error: ", ecode.message (), ". Check your clock");
if (ecode != boost::asio::error::operation_aborted)
{
// this router doesn't like us
@@ -450,7 +451,6 @@ namespace transport
}
else
{
LogPrint (eLogDebug, "Phase 4 received: ", bytes_transferred);
m_Decryption.Decrypt(m_ReceiveBuffer, bytes_transferred, m_ReceiveBuffer);
// verify signature
@@ -467,7 +467,7 @@ namespace transport
Terminate ();
return;
}
LogPrint ("NTCP session connected");
LogPrint (eLogInfo, "NTCP session to ", m_Socket.remote_endpoint (), " connected");
Connected ();
m_ReceiveBufferOffset = 0;
@@ -488,6 +488,7 @@ namespace transport
if (ecode)
{
LogPrint (eLogError, "Read error: ", ecode.message ());
if (!m_NumReceivedBytes) m_Server.Ban (m_ConnectedFrom);
//if (ecode != boost::asio::error::operation_aborted)
Terminate ();
}
@@ -498,19 +499,47 @@ namespace transport
if (m_ReceiveBufferOffset >= 16)
{
uint8_t * nextBlock = m_ReceiveBuffer;
while (m_ReceiveBufferOffset >= 16)
{
if (!DecryptNextBlock (nextBlock)) // 16 bytes
int numReloads = 0;
do
{
uint8_t * nextBlock = m_ReceiveBuffer;
while (m_ReceiveBufferOffset >= 16)
{
Terminate ();
return;
if (!DecryptNextBlock (nextBlock)) // 16 bytes
{
Terminate ();
return;
}
nextBlock += 16;
m_ReceiveBufferOffset -= 16;
}
if (m_ReceiveBufferOffset > 0)
memcpy (m_ReceiveBuffer, nextBlock, m_ReceiveBufferOffset);
// try to read more
if (numReloads < 5)
{
boost::system::error_code ec;
size_t moreBytes = m_Socket.available(ec);
if (moreBytes)
{
if (moreBytes > NTCP_BUFFER_SIZE - m_ReceiveBufferOffset)
moreBytes = NTCP_BUFFER_SIZE - m_ReceiveBufferOffset;
moreBytes = m_Socket.read_some (boost::asio::buffer (m_ReceiveBuffer + m_ReceiveBufferOffset, moreBytes));
if (ec)
{
LogPrint (eLogError, "Read more bytes error: ", ec.message ());
Terminate ();
return;
}
m_NumReceivedBytes += moreBytes;
m_ReceiveBufferOffset += moreBytes;
numReloads++;
}
}
nextBlock += 16;
m_ReceiveBufferOffset -= 16;
}
if (m_ReceiveBufferOffset > 0)
memcpy (m_ReceiveBuffer, nextBlock, m_ReceiveBufferOffset);
while (m_ReceiveBufferOffset >= 16);
m_Handler.Flush ();
}
ScheduleTermination (); // reset termination timer
@@ -559,13 +588,20 @@ namespace transport
if (m_NextMessageOffset >= m_NextMessage->len + 4) // +checksum
{
// we have a complete I2NP message
i2p::HandleI2NPMessage (m_NextMessage);
m_Handler.PutNextMessage (m_NextMessage);
m_NextMessage = nullptr;
}
return true;
}
void NTCPSession::Send (i2p::I2NPMessage * msg)
{
m_IsSending = true;
boost::asio::async_write (m_Socket, CreateMsgBuffer (msg), boost::asio::transfer_all (),
std::bind(&NTCPSession::HandleSent, shared_from_this (), std::placeholders::_1, std::placeholders::_2, std::vector<I2NPMessage *>{ msg }));
}
boost::asio::const_buffers_1 NTCPSession::CreateMsgBuffer (I2NPMessage * msg)
{
uint8_t * sendBuffer;
int len;
@@ -598,18 +634,28 @@ namespace transport
int l = len + padding + 6;
m_Encryption.Encrypt(sendBuffer, l, sendBuffer);
return boost::asio::buffer ((const uint8_t *)sendBuffer, l);
}
boost::asio::async_write (m_Socket, boost::asio::buffer (sendBuffer, l), boost::asio::transfer_all (),
std::bind(&NTCPSession::HandleSent, shared_from_this (), std::placeholders::_1, std::placeholders::_2, msg));
void NTCPSession::Send (const std::vector<I2NPMessage *>& msgs)
{
m_IsSending = true;
std::vector<boost::asio::const_buffer> bufs;
for (auto it: msgs)
bufs.push_back (CreateMsgBuffer (it));
boost::asio::async_write (m_Socket, bufs, boost::asio::transfer_all (),
std::bind(&NTCPSession::HandleSent, shared_from_this (), std::placeholders::_1, std::placeholders::_2, msgs));
}
void NTCPSession::HandleSent (const boost::system::error_code& ecode, std::size_t bytes_transferred, i2p::I2NPMessage * msg)
{
if (msg)
i2p::DeleteI2NPMessage (msg);
void NTCPSession::HandleSent (const boost::system::error_code& ecode, std::size_t bytes_transferred, std::vector<I2NPMessage *> msgs)
{
m_IsSending = false;
for (auto it: msgs)
if (it) i2p::DeleteI2NPMessage (it);
if (ecode)
{
LogPrint (eLogWarning, "Couldn't send msg: ", ecode.message ());
LogPrint (eLogWarning, "Couldn't send msgs: ", ecode.message ());
// we shouldn't call Terminate () here, because HandleReceive takes care
// TODO: 'delete this' statement in Terminate () must be eliminated later
// Terminate ();
@@ -617,26 +663,65 @@ namespace transport
else
{
m_NumSentBytes += bytes_transferred;
ScheduleTermination (); // reset termination timer
if (!m_SendQueue.empty())
{
Send (m_SendQueue);
m_SendQueue.clear ();
}
else
ScheduleTermination (); // reset termination timer
}
}
}
void NTCPSession::SendTimeSyncMessage ()
{
Send (nullptr);
}
void NTCPSession::SendI2NPMessage (I2NPMessage * msg)
{
m_Server.GetService ().post (std::bind (&NTCPSession::PostI2NPMessage, shared_from_this (), msg));
}
void NTCPSession::PostI2NPMessage (I2NPMessage * msg)
{
if (msg)
{
if (m_IsEstablished)
if (m_IsTerminated)
{
DeleteI2NPMessage (msg);
return;
}
if (m_IsSending)
m_SendQueue.push_back (msg);
else
Send (msg);
else
m_DelayedMessages.push_back (msg);
}
}
void NTCPSession::SendI2NPMessages (const std::vector<I2NPMessage *>& msgs)
{
m_Server.GetService ().post (std::bind (&NTCPSession::PostI2NPMessages, shared_from_this (), msgs));
}
void NTCPSession::PostI2NPMessages (std::vector<I2NPMessage *> msgs)
{
if (m_IsTerminated)
{
for (auto it: msgs)
DeleteI2NPMessage (it);
return;
}
if (m_IsSending)
{
for (auto it: msgs)
m_SendQueue.push_back (it);
}
else
Send (msgs);
}
void NTCPSession::ScheduleTermination ()
{
m_TerminationTimer.cancel ();
@@ -654,5 +739,229 @@ namespace transport
m_Socket.close ();// invoke Terminate () from HandleReceive
}
}
//-----------------------------------------
NTCPServer::NTCPServer (int port):
m_IsRunning (false), m_Thread (nullptr), m_Work (m_Service),
m_NTCPAcceptor (nullptr), m_NTCPV6Acceptor (nullptr)
{
}
NTCPServer::~NTCPServer ()
{
Stop ();
}
void NTCPServer::Start ()
{
if (!m_IsRunning)
{
m_IsRunning = true;
m_Thread = new std::thread (std::bind (&NTCPServer::Run, this));
// create acceptors
auto addresses = context.GetRouterInfo ().GetAddresses ();
for (auto& address : addresses)
{
if (address.transportStyle == i2p::data::RouterInfo::eTransportNTCP && address.host.is_v4 ())
{
m_NTCPAcceptor = new boost::asio::ip::tcp::acceptor (m_Service,
boost::asio::ip::tcp::endpoint(boost::asio::ip::tcp::v4(), address.port));
LogPrint (eLogInfo, "Start listening TCP port ", address.port);
auto conn = std::make_shared<NTCPSession>(*this);
m_NTCPAcceptor->async_accept(conn->GetSocket (), std::bind (&NTCPServer::HandleAccept, this,
conn, std::placeholders::_1));
if (context.SupportsV6 ())
{
m_NTCPV6Acceptor = new boost::asio::ip::tcp::acceptor (m_Service);
m_NTCPV6Acceptor->open (boost::asio::ip::tcp::v6());
m_NTCPV6Acceptor->set_option (boost::asio::ip::v6_only (true));
m_NTCPV6Acceptor->bind (boost::asio::ip::tcp::endpoint(boost::asio::ip::tcp::v6(), address.port));
m_NTCPV6Acceptor->listen ();
LogPrint (eLogInfo, "Start listening V6 TCP port ", address.port);
auto conn = std::make_shared<NTCPSession> (*this);
m_NTCPV6Acceptor->async_accept(conn->GetSocket (), std::bind (&NTCPServer::HandleAcceptV6,
this, conn, std::placeholders::_1));
}
}
}
}
}
void NTCPServer::Stop ()
{
m_NTCPSessions.clear ();
if (m_IsRunning)
{
m_IsRunning = false;
delete m_NTCPAcceptor;
m_NTCPAcceptor = nullptr;
delete m_NTCPV6Acceptor;
m_NTCPV6Acceptor = nullptr;
m_Service.stop ();
if (m_Thread)
{
m_Thread->join ();
delete m_Thread;
m_Thread = nullptr;
}
}
}
void NTCPServer::Run ()
{
while (m_IsRunning)
{
try
{
m_Service.run ();
}
catch (std::exception& ex)
{
LogPrint ("NTCP server: ", ex.what ());
}
}
}
void NTCPServer::AddNTCPSession (std::shared_ptr<NTCPSession> session)
{
if (session)
{
std::unique_lock<std::mutex> l(m_NTCPSessionsMutex);
m_NTCPSessions[session->GetRemoteIdentity ().GetIdentHash ()] = session;
}
}
void NTCPServer::RemoveNTCPSession (std::shared_ptr<NTCPSession> session)
{
if (session)
{
std::unique_lock<std::mutex> l(m_NTCPSessionsMutex);
m_NTCPSessions.erase (session->GetRemoteIdentity ().GetIdentHash ());
}
}
std::shared_ptr<NTCPSession> NTCPServer::FindNTCPSession (const i2p::data::IdentHash& ident)
{
std::unique_lock<std::mutex> l(m_NTCPSessionsMutex);
auto it = m_NTCPSessions.find (ident);
if (it != m_NTCPSessions.end ())
return it->second;
return nullptr;
}
void NTCPServer::HandleAccept (std::shared_ptr<NTCPSession> conn, const boost::system::error_code& error)
{
if (!error)
{
boost::system::error_code ec;
auto ep = conn->GetSocket ().remote_endpoint(ec);
if (!ec)
{
LogPrint (eLogInfo, "Connected from ", ep);
auto it = m_BanList.find (ep.address ());
if (it != m_BanList.end ())
{
uint32_t ts = i2p::util::GetSecondsSinceEpoch ();
if (ts < it->second)
{
LogPrint (eLogInfo, ep.address (), " is banned for ", it->second - ts, " more seconds");
conn = nullptr;
}
else
m_BanList.erase (it);
}
if (conn)
conn->ServerLogin ();
}
else
LogPrint (eLogError, "Connected from error ", ec.message ());
}
if (error != boost::asio::error::operation_aborted)
{
conn = std::make_shared<NTCPSession> (*this);
m_NTCPAcceptor->async_accept(conn->GetSocket (), std::bind (&NTCPServer::HandleAccept, this,
conn, std::placeholders::_1));
}
}
void NTCPServer::HandleAcceptV6 (std::shared_ptr<NTCPSession> conn, const boost::system::error_code& error)
{
if (!error)
{
boost::system::error_code ec;
auto ep = conn->GetSocket ().remote_endpoint(ec);
if (!ec)
{
LogPrint (eLogInfo, "Connected from ", ep);
auto it = m_BanList.find (ep.address ());
if (it != m_BanList.end ())
{
uint32_t ts = i2p::util::GetSecondsSinceEpoch ();
if (ts < it->second)
{
LogPrint (eLogInfo, ep.address (), " is banned for ", it->second - ts, " more seconds");
conn = nullptr;
}
else
m_BanList.erase (it);
}
if (conn)
conn->ServerLogin ();
}
else
LogPrint (eLogError, "Connected from error ", ec.message ());
}
if (error != boost::asio::error::operation_aborted)
{
conn = std::make_shared<NTCPSession> (*this);
m_NTCPV6Acceptor->async_accept(conn->GetSocket (), std::bind (&NTCPServer::HandleAcceptV6, this,
conn, std::placeholders::_1));
}
}
void NTCPServer::Connect (const boost::asio::ip::address& address, int port, std::shared_ptr<NTCPSession> conn)
{
LogPrint (eLogInfo, "Connecting to ", address ,":", port);
m_Service.post([conn, this]()
{
this->AddNTCPSession (conn);
});
conn->GetSocket ().async_connect (boost::asio::ip::tcp::endpoint (address, port),
std::bind (&NTCPServer::HandleConnect, this, std::placeholders::_1, conn));
}
void NTCPServer::HandleConnect (const boost::system::error_code& ecode, std::shared_ptr<NTCPSession> conn)
{
if (ecode)
{
LogPrint (eLogError, "Connect error: ", ecode.message ());
if (ecode != boost::asio::error::operation_aborted)
i2p::data::netdb.SetUnreachable (conn->GetRemoteIdentity ().GetIdentHash (), true);
conn->Terminate ();
}
else
{
LogPrint (eLogInfo, "Connected to ", conn->GetSocket ().remote_endpoint ());
if (conn->GetSocket ().local_endpoint ().protocol () == boost::asio::ip::tcp::v6()) // ipv6
context.UpdateNTCPV6Address (conn->GetSocket ().local_endpoint ().address ());
conn->ClientLogin ();
}
}
void NTCPServer::Ban (const boost::asio::ip::address& addr)
{
uint32_t ts = i2p::util::GetSecondsSinceEpoch ();
m_BanList[addr] = ts + NTCP_BAN_EXPIRATION_TIMEOUT;
LogPrint (eLogInfo, addr, " has been banned for ", NTCP_BAN_EXPIRATION_TIMEOUT, " seconds");
}
}
}

View File

@@ -2,8 +2,11 @@
#define NTCP_SESSION_H__
#include <inttypes.h>
#include <list>
#include <map>
#include <memory>
#include <thread>
#include <mutex>
#include <boost/asio.hpp>
#include <cryptopp/modes.h>
#include <cryptopp/aes.h>
#include <cryptopp/adler32.h>
@@ -39,17 +42,20 @@ namespace transport
#pragma pack()
const size_t NTCP_MAX_MESSAGE_SIZE = 16384;
const size_t NTCP_BUFFER_SIZE = 1040; // fits one tunnel message (1028)
const size_t NTCP_BUFFER_SIZE = 4160; // fits 4 tunnel messages (4*1028)
const int NTCP_TERMINATION_TIMEOUT = 120; // 2 minutes
const size_t NTCP_DEFAULT_PHASE3_SIZE = 2/*size*/ + i2p::data::DEFAULT_IDENTITY_SIZE/*387*/ + 4/*ts*/ + 15/*padding*/ + 40/*signature*/; // 448
const int NTCP_BAN_EXPIRATION_TIMEOUT = 70; // in second
class NTCPServer;
class NTCPSession: public TransportSession, public std::enable_shared_from_this<NTCPSession>
{
public:
NTCPSession (boost::asio::io_service& service, std::shared_ptr<const i2p::data::RouterInfo> in_RemoteRouter = nullptr);
NTCPSession (NTCPServer& server, std::shared_ptr<const i2p::data::RouterInfo> in_RemoteRouter = nullptr);
~NTCPSession ();
void Terminate ();
void Done ();
boost::asio::ip::tcp::socket& GetSocket () { return m_Socket; };
bool IsEstablished () const { return m_IsEstablished; };
@@ -57,17 +63,18 @@ namespace transport
void ClientLogin ();
void ServerLogin ();
void SendI2NPMessage (I2NPMessage * msg);
void SendI2NPMessages (const std::vector<I2NPMessage *>& msgs);
size_t GetNumSentBytes () const { return m_NumSentBytes; };
size_t GetNumReceivedBytes () const { return m_NumReceivedBytes; };
protected:
private:
void PostI2NPMessage (I2NPMessage * msg);
void PostI2NPMessages (std::vector<I2NPMessage *> msgs);
void Connected ();
void SendTimeSyncMessage ();
void SetIsEstablished (bool isEstablished) { m_IsEstablished = isEstablished; }
private:
void CreateAESKey (uint8_t * pubKey, i2p::crypto::AESKey& key);
@@ -94,18 +101,21 @@ namespace transport
bool DecryptNextBlock (const uint8_t * encrypted);
void Send (i2p::I2NPMessage * msg);
void HandleSent (const boost::system::error_code& ecode, std::size_t bytes_transferred, i2p::I2NPMessage * msg);
boost::asio::const_buffers_1 CreateMsgBuffer (I2NPMessage * msg);
void Send (const std::vector<I2NPMessage *>& msgs);
void HandleSent (const boost::system::error_code& ecode, std::size_t bytes_transferred, std::vector<I2NPMessage *> msgs);
// timer
void ScheduleTermination ();
void HandleTerminationTimer (const boost::system::error_code& ecode);
private:
NTCPServer& m_Server;
boost::asio::ip::tcp::socket m_Socket;
boost::asio::deadline_timer m_TerminationTimer;
bool m_IsEstablished;
bool m_IsEstablished, m_IsTerminated;
i2p::crypto::CBCDecryption m_Decryption;
i2p::crypto::CBCEncryption m_Encryption;
@@ -122,10 +132,58 @@ namespace transport
int m_ReceiveBufferOffset;
i2p::I2NPMessage * m_NextMessage;
std::list<i2p::I2NPMessage *> m_DelayedMessages;
size_t m_NextMessageOffset;
i2p::I2NPMessagesHandler m_Handler;
bool m_IsSending;
std::vector<I2NPMessage *> m_SendQueue;
size_t m_NumSentBytes, m_NumReceivedBytes;
boost::asio::ip::address m_ConnectedFrom; // for ban
};
// TODO: move to NTCP.h/.cpp
class NTCPServer
{
public:
NTCPServer (int port);
~NTCPServer ();
void Start ();
void Stop ();
void AddNTCPSession (std::shared_ptr<NTCPSession> session);
void RemoveNTCPSession (std::shared_ptr<NTCPSession> session);
std::shared_ptr<NTCPSession> FindNTCPSession (const i2p::data::IdentHash& ident);
void Connect (const boost::asio::ip::address& address, int port, std::shared_ptr<NTCPSession> conn);
boost::asio::io_service& GetService () { return m_Service; };
void Ban (const boost::asio::ip::address& addr);
private:
void Run ();
void HandleAccept (std::shared_ptr<NTCPSession> conn, const boost::system::error_code& error);
void HandleAcceptV6 (std::shared_ptr<NTCPSession> conn, const boost::system::error_code& error);
void HandleConnect (const boost::system::error_code& ecode, std::shared_ptr<NTCPSession> conn);
private:
bool m_IsRunning;
std::thread * m_Thread;
boost::asio::io_service m_Service;
boost::asio::io_service::work m_Work;
boost::asio::ip::tcp::acceptor * m_NTCPAcceptor, * m_NTCPV6Acceptor;
std::mutex m_NTCPSessionsMutex;
std::map<i2p::data::IdentHash, std::shared_ptr<NTCPSession> > m_NTCPSessions;
std::map<boost::asio::ip::address, uint32_t> m_BanList; // IP -> ban expiration time in seconds
public:
// for HTTP/I2PControl
const decltype(m_NTCPSessions)& GetNTCPSessions () const { return m_NTCPSessions; };
};
}
}

511
NetDb.cpp
View File

@@ -13,7 +13,6 @@
#include "RouterContext.h"
#include "Garlic.h"
#include "NetDb.h"
#include "Reseed.h"
#include "util.h"
using namespace i2p::transport;
@@ -23,13 +22,12 @@ namespace i2p
namespace data
{
I2NPMessage * RequestedDestination::CreateRequestMessage (std::shared_ptr<const RouterInfo> router,
const i2p::tunnel::InboundTunnel * replyTunnel)
std::shared_ptr<const i2p::tunnel::InboundTunnel> replyTunnel)
{
I2NPMessage * msg = i2p::CreateRouterInfoDatabaseLookupMsg (m_Destination,
replyTunnel->GetNextIdentHash (), replyTunnel->GetNextTunnelID (), m_IsExploratory,
&m_ExcludedPeers);
m_ExcludedPeers.insert (router->GetIdentHash ());
m_LastRouter = router;
m_CreationTime = i2p::util::GetSecondsSinceEpoch ();
return msg;
}
@@ -39,7 +37,6 @@ namespace data
I2NPMessage * msg = i2p::CreateRouterInfoDatabaseLookupMsg (m_Destination,
i2p::context.GetRouterInfo ().GetIdentHash () , 0, false, &m_ExcludedPeers);
m_ExcludedPeers.insert (floodfill);
m_LastRouter = nullptr;
m_CreationTime = i2p::util::GetSecondsSinceEpoch ();
return msg;
}
@@ -49,6 +46,24 @@ namespace data
m_ExcludedPeers.clear ();
}
void RequestedDestination::Success (std::shared_ptr<RouterInfo> r)
{
if (m_RequestComplete)
{
m_RequestComplete (r);
m_RequestComplete = nullptr;
}
}
void RequestedDestination::Fail ()
{
if (m_RequestComplete)
{
m_RequestComplete (nullptr);
m_RequestComplete = nullptr;
}
}
#ifndef _WIN32
const char NetDb::m_NetDbPath[] = "/netDb";
#else
@@ -56,17 +71,14 @@ namespace data
#endif
NetDb netdb;
NetDb::NetDb (): m_IsRunning (false), m_Thread (nullptr)
NetDb::NetDb (): m_IsRunning (false), m_Thread (nullptr), m_Reseeder (nullptr)
{
}
NetDb::~NetDb ()
{
Stop ();
for (auto l:m_LeaseSets)
delete l.second;
for (auto r:m_RequestedDestinations)
delete r.second;
delete m_Reseeder;
}
void NetDb::Start ()
@@ -74,26 +86,23 @@ namespace data
Load (m_NetDbPath);
if (m_RouterInfos.size () < 50) // reseed if # of router less than 50
{
Reseeder reseeder;
reseeder.LoadCertificates (); // we need certificates for SU3 verification
// try SU3 first
int reseedRetries = 0;
while (m_RouterInfos.size () < 50 && reseedRetries < 10)
{
reseeder.ReseedNowSU3();
reseedRetries++;
}
Reseed ();
// if still not enough download .dat files
reseedRetries = 0;
while (m_RouterInfos.size () < 50 && reseedRetries < 10)
// deprecated
if (m_Reseeder)
{
reseeder.reseedNow();
reseedRetries++;
Load (m_NetDbPath);
// if still not enough download .dat files
int reseedRetries = 0;
while (m_RouterInfos.size () < 50 && reseedRetries < 10)
{
m_Reseeder->reseedNow();
reseedRetries++;
Load (m_NetDbPath);
}
}
}
m_IsRunning = true;
m_Thread = new std::thread (std::bind (&NetDb::Run, this));
}
@@ -106,13 +115,14 @@ namespace data
m_Thread->join ();
delete m_Thread;
m_Thread = 0;
}
}
m_LeaseSets.clear();
m_RequestedDestinations.clear ();
}
void NetDb::Run ()
{
uint32_t lastSave = 0, lastPublish = 0;
m_IsRunning = true;
uint32_t lastSave = 0, lastPublish = 0, lastExploratory = 0, lastManageRequest = 0;
while (m_IsRunning)
{
try
@@ -120,6 +130,7 @@ namespace data
I2NPMessage * msg = m_Queue.GetNextWithTimeout (15000); // 15 sec
if (msg)
{
int numMsgs = 0;
while (msg)
{
switch (msg->GetTypeID ())
@@ -140,19 +151,19 @@ namespace data
LogPrint ("NetDb: unexpected message type ", msg->GetTypeID ());
i2p::HandleI2NPMessage (msg);
}
if (numMsgs > 100) break;
msg = m_Queue.Get ();
numMsgs++;
}
}
else
{
if (!m_IsRunning) break;
// if no new DatabaseStore coming, explore it
ManageRequests ();
auto numRouters = m_RouterInfos.size ();
Explore (numRouters < 1500 ? 5 : 1);
}
}
if (!m_IsRunning) break;
uint64_t ts = i2p::util::GetSecondsSinceEpoch ();
if (ts - lastManageRequest >= 15) // manage requests every 15 seconds
{
ManageRequests ();
lastManageRequest = ts;
}
if (ts - lastSave >= 60) // save routers, manage leasesets and validate subscriptions every minute
{
if (lastSave)
@@ -162,11 +173,24 @@ namespace data
}
lastSave = ts;
}
if (ts - lastPublish >= 600) // publish every 10 minutes
if (ts - lastPublish >= 2400) // publish every 40 minutes
{
Publish ();
lastPublish = ts;
}
if (ts - lastExploratory >= 30) // exploratory every 30 seconds
{
auto numRouters = m_RouterInfos.size ();
if (numRouters < 2500 || ts - lastExploratory >= 90)
{
numRouters = 800/numRouters;
if (numRouters < 1) numRouters = 1;
if (numRouters > 9) numRouters = 9;
ManageRequests ();
Explore (numRouters);
lastExploratory = ts;
}
}
}
catch (std::exception& ex)
{
@@ -184,7 +208,6 @@ namespace data
void NetDb::AddRouterInfo (const IdentHash& ident, const uint8_t * buf, int len)
{
DeleteRequestedDestination (ident);
auto r = FindRouter (ident);
if (r)
{
@@ -196,23 +219,30 @@ namespace data
else
{
LogPrint ("New RouterInfo added");
auto newRouter = std::make_shared<RouterInfo> (buf, len);
r = std::make_shared<RouterInfo> (buf, len);
{
std::unique_lock<std::mutex> l(m_RouterInfosMutex);
m_RouterInfos[newRouter->GetIdentHash ()] = newRouter;
m_RouterInfos[r->GetIdentHash ()] = r;
}
if (newRouter->IsFloodfill ())
if (r->IsFloodfill ())
{
std::unique_lock<std::mutex> l(m_FloodfillsMutex);
m_Floodfills.push_back (newRouter);
m_Floodfills.push_back (r);
}
}
// take care about requested destination
auto it = m_RequestedDestinations.find (ident);
if (it != m_RequestedDestinations.end ())
{
it->second->Success (r);
std::unique_lock<std::mutex> l(m_RequestedDestinationsMutex);
m_RequestedDestinations.erase (it);
}
}
void NetDb::AddLeaseSet (const IdentHash& ident, const uint8_t * buf, int len,
i2p::tunnel::InboundTunnel * from)
std::shared_ptr<i2p::tunnel::InboundTunnel> from)
{
DeleteRequestedDestination (ident);
if (!from) // unsolicited LS must be received directly
{
auto it = m_LeaseSets.find(ident);
@@ -224,7 +254,7 @@ namespace data
else
{
LogPrint ("New LeaseSet added");
m_LeaseSets[ident] = new LeaseSet (buf, len);
m_LeaseSets[ident] = std::make_shared<LeaseSet> (buf, len);
}
}
}
@@ -239,7 +269,7 @@ namespace data
return nullptr;
}
LeaseSet * NetDb::FindLeaseSet (const IdentHash& destination) const
std::shared_ptr<LeaseSet> NetDb::FindLeaseSet (const IdentHash& destination) const
{
auto it = m_LeaseSets.find (destination);
if (it != m_LeaseSets.end ())
@@ -280,6 +310,20 @@ namespace data
return true;
}
void NetDb::Reseed ()
{
if (!m_Reseeder)
{
m_Reseeder = new Reseeder ();
m_Reseeder->LoadCertificates (); // we need certificates for SU3 verification
}
int reseedRetries = 0;
while (reseedRetries < 10 && !m_Reseeder->ReseedNowSU3 ())
reseedRetries++;
if (reseedRetries >= 10)
LogPrint (eLogWarning, "Failed to reseed after 10 attempts");
}
void NetDb::Load (const char * directory)
{
boost::filesystem::path p (i2p::util::filesystem::GetDataDir());
@@ -359,6 +403,7 @@ namespace data
{
it.second->SaveToFile (GetFilePath(fullDirectory, it.second.get ()));
it.second->SetUpdated (false);
it.second->SetUnreachable (false);
it.second->DeleteBuffer ();
count++;
}
@@ -407,17 +452,30 @@ namespace data
}
}
void NetDb::RequestDestination (const IdentHash& destination)
void NetDb::RequestDestination (const IdentHash& destination, RequestedDestination::RequestComplete requestComplete)
{
// request RouterInfo directly
RequestedDestination * dest = CreateRequestedDestination (destination, false);
auto dest = new RequestedDestination (destination, false); // non-exploratory
dest->SetRequestComplete (requestComplete);
{
std::unique_lock<std::mutex> l(m_RequestedDestinationsMutex);
if (!m_RequestedDestinations.insert (std::make_pair (destination,
std::unique_ptr<RequestedDestination> (dest))).second) // not inserted
{
LogPrint (eLogWarning, "Destination ", destination.ToBase64(), " is requested already");
return;
}
}
auto floodfill = GetClosestFloodfill (destination, dest->GetExcludedPeers ());
if (floodfill)
transports.SendMessage (floodfill->GetIdentHash (), dest->CreateRequestMessage (floodfill->GetIdentHash ()));
else
{
LogPrint (eLogError, "No floodfills found");
DeleteRequestedDestination (dest);
dest->Fail ();
std::unique_lock<std::mutex> l(m_RequestedDestinationsMutex);
m_RequestedDestinations.erase (destination);
}
}
@@ -428,7 +486,49 @@ namespace data
uint32_t replyToken = bufbe32toh (buf + DATABASE_STORE_REPLY_TOKEN_OFFSET);
size_t offset = DATABASE_STORE_HEADER_SIZE;
if (replyToken)
offset += 36;
{
auto deliveryStatus = CreateDeliveryStatusMsg (replyToken);
uint32_t tunnelID = bufbe32toh (buf + offset);
offset += 4;
if (!tunnelID) // send response directly
transports.SendMessage (buf + offset, deliveryStatus);
else
{
auto pool = i2p::tunnel::tunnels.GetExploratoryPool ();
auto outbound = pool ? pool->GetNextOutboundTunnel () : nullptr;
if (outbound)
outbound->SendTunnelDataMsg (buf + offset, tunnelID, deliveryStatus);
else
{
LogPrint (eLogError, "No outbound tunnels for DatabaseStore reply found");
DeleteI2NPMessage (deliveryStatus);
}
}
offset += 32;
if (context.IsFloodfill ())
{
// flood it
std::set<IdentHash> excluded;
for (int i = 0; i < 3; i++)
{
auto floodfill = GetClosestFloodfill (buf + DATABASE_STORE_KEY_OFFSET, excluded);
if (floodfill)
{
excluded.insert (floodfill->GetIdentHash ());
auto floodMsg = NewI2NPShortMessage ();
uint8_t * payload = floodMsg->GetPayload ();
memcpy (payload, buf, 33); // key + type
htobe32buf (payload + DATABASE_STORE_REPLY_TOKEN_OFFSET, 0); // zero reply token
memcpy (payload + DATABASE_STORE_HEADER_SIZE, buf + offset, len - offset);
floodMsg->len += DATABASE_STORE_HEADER_SIZE + len -offset;
FillI2NPMessageHeader (floodMsg, eI2NPDatabaseStore);
transports.SendMessage (floodfill->GetIdentHash (), floodMsg);
}
}
}
}
if (buf[DATABASE_STORE_TYPE_OFFSET]) // type
{
LogPrint ("LeaseSet");
@@ -438,19 +538,27 @@ namespace data
{
LogPrint ("RouterInfo");
size_t size = bufbe16toh (buf + offset);
if (size > 2048)
offset += 2;
if (size > 2048 || size > len - offset)
{
LogPrint ("Invalid RouterInfo length ", (int)size);
i2p::DeleteI2NPMessage (m);
return;
}
offset += 2;
CryptoPP::Gunzip decompressor;
decompressor.Put (buf + offset, size);
decompressor.MessageEnd();
uint8_t uncompressed[2048];
size_t uncomressedSize = decompressor.MaxRetrievable ();
decompressor.Get (uncompressed, uncomressedSize);
AddRouterInfo (buf + DATABASE_STORE_KEY_OFFSET, uncompressed, uncomressedSize);
try
{
CryptoPP::Gunzip decompressor;
decompressor.Put (buf + offset, size);
decompressor.MessageEnd();
uint8_t uncompressed[2048];
size_t uncomressedSize = decompressor.MaxRetrievable ();
decompressor.Get (uncompressed, uncomressedSize);
AddRouterInfo (buf + DATABASE_STORE_KEY_OFFSET, uncompressed, uncomressedSize);
}
catch (CryptoPP::Exception& ex)
{
LogPrint (eLogError, "DatabaseStore: ", ex.what ());
}
}
i2p::DeleteI2NPMessage (m);
}
@@ -466,19 +574,19 @@ namespace data
auto it = m_RequestedDestinations.find (IdentHash (buf));
if (it != m_RequestedDestinations.end ())
{
RequestedDestination * dest = it->second;
auto& dest = it->second;
bool deleteDest = true;
if (num > 0)
{
auto pool = i2p::tunnel::tunnels.GetExploratoryPool ();
auto outbound = pool->GetNextOutboundTunnel ();
auto inbound = pool->GetNextInboundTunnel ();
std::vector<i2p::tunnel::TunnelMessageBlock> msgs;
auto outbound = pool ? pool->GetNextOutboundTunnel () : nullptr;
auto inbound = pool ? pool->GetNextInboundTunnel () : nullptr;
if (!dest->IsExploratory ())
{
// reply to our destination. Try other floodfills
if (outbound && inbound )
{
std::vector<i2p::tunnel::TunnelMessageBlock> msgs;
auto count = dest->GetExcludedPeers ().size ();
if (count < 7)
{
@@ -506,83 +614,49 @@ namespace data
}
else
LogPrint (key, " was not found on 7 floodfills");
if (msgs.size () > 0)
outbound->SendTunnelDataMsg (msgs);
}
}
for (int i = 0; i < num; i++)
{
uint8_t * router = buf + 33 + i*32;
char peerHash[48];
int l1 = i2p::data::ByteStreamToBase64 (router, 32, peerHash, 48);
peerHash[l1] = 0;
LogPrint (i,": ", peerHash);
if (dest->IsExploratory ())
{
auto r = FindRouter (router);
if (!r || i2p::util::GetMillisecondsSinceEpoch () > r->GetTimestamp () + 3600*1000LL)
{
// router with ident not found or too old (1 hour)
LogPrint ("Found new/outdated router. Requesting RouterInfo ...");
if (outbound && inbound && dest->GetLastRouter ())
{
RequestedDestination * d1 = CreateRequestedDestination (router, false);
auto msg = d1->CreateRequestMessage (dest->GetLastRouter (), inbound);
msgs.push_back (i2p::tunnel::TunnelMessageBlock
{
i2p::tunnel::eDeliveryTypeRouter,
dest->GetLastRouter ()->GetIdentHash (), 0, msg
});
}
else
RequestDestination (router);
}
else
LogPrint ("Bayan");
}
else
{
auto r = FindRouter (router);
// do we have that floodfill router in our database?
if (!r)
{
// request router
LogPrint ("Found new floodfill. Request it");
RequestDestination (router);
}
}
}
if (outbound && msgs.size () > 0)
outbound->SendTunnelDataMsg (msgs);
if (deleteDest)
{
// no more requests for the destinationation. delete it
delete it->second;
it->second->Fail ();
m_RequestedDestinations.erase (it);
}
}
else
{
// no more requests for detination possible. delete it
delete it->second;
it->second->Fail ();
m_RequestedDestinations.erase (it);
}
}
else
{
else
LogPrint ("Requested destination for ", key, " not found");
// it might contain new routers
for (int i = 0; i < num; i++)
{
IdentHash router (buf + 33 + i*32);
if (!FindRouter (router))
{
LogPrint ("New router ", router.ToBase64 (), " found. Request it");
RequestDestination (router);
}
}
// try responses
for (int i = 0; i < num; i++)
{
uint8_t * router = buf + 33 + i*32;
char peerHash[48];
int l1 = i2p::data::ByteStreamToBase64 (router, 32, peerHash, 48);
peerHash[l1] = 0;
LogPrint (i,": ", peerHash);
auto r = FindRouter (router);
if (!r || i2p::util::GetMillisecondsSinceEpoch () > r->GetTimestamp () + 3600*1000LL)
{
// router with ident not found or too old (1 hour)
LogPrint ("Found new/outdated router. Requesting RouterInfo ...");
RequestDestination (router);
}
else
LogPrint ("Bayan");
}
i2p::DeleteI2NPMessage (msg);
}
@@ -592,11 +666,12 @@ namespace data
char key[48];
int l = i2p::data::ByteStreamToBase64 (buf, 32, key, 48);
key[l] = 0;
LogPrint ("DatabaseLookup for ", key, " recieved");
uint8_t flag = buf[64];
LogPrint ("DatabaseLookup for ", key, " recieved flags=", (int)flag);
uint8_t lookupType = flag & DATABASE_LOOKUP_TYPE_FLAGS_MASK;
uint8_t * excluded = buf + 65;
uint32_t replyTunnelID = 0;
if (flag & 0x01) //reply to tunnel
if (flag & DATABASE_LOOKUP_DELIVERY_FLAG) //reply to tunnel
{
replyTunnelID = bufbe32toh (buf + 64);
excluded += 4;
@@ -608,10 +683,31 @@ namespace data
LogPrint ("Number of excluded peers", numExcluded, " exceeds 512");
numExcluded = 0; // TODO:
}
I2NPMessage * replyMsg = nullptr;
if (lookupType == DATABASE_LOOKUP_TYPE_EXPLORATORY_LOOKUP)
{
LogPrint ("Exploratory close to ", key, " ", numExcluded, " excluded");
std::set<IdentHash> excludedRouters;
for (int i = 0; i < numExcluded; i++)
{
excludedRouters.insert (excluded);
excluded += 32;
}
std::vector<IdentHash> routers;
for (int i = 0; i < 3; i++)
{
auto r = GetClosestNonFloodfill (buf, excludedRouters);
if (r)
{
routers.push_back (r->GetIdentHash ());
excludedRouters.insert (r->GetIdentHash ());
}
}
replyMsg = CreateDatabaseSearchReply (buf, routers);
}
else
{
auto router = FindRouter (buf);
if (router)
{
@@ -620,37 +716,45 @@ namespace data
if (router->GetBuffer ())
replyMsg = CreateDatabaseStoreMsg (router.get ());
}
}
if (!replyMsg)
{
auto leaseSet = FindLeaseSet (buf);
if (leaseSet) // we don't send back our LeaseSets
if (!replyMsg)
{
LogPrint ("Requested LeaseSet ", key, " found");
replyMsg = CreateDatabaseStoreMsg (leaseSet);
auto leaseSet = FindLeaseSet (buf);
if (leaseSet) // we don't send back our LeaseSets
{
LogPrint ("Requested LeaseSet ", key, " found");
replyMsg = CreateDatabaseStoreMsg (leaseSet.get ());
}
}
if (!replyMsg)
{
LogPrint ("Requested ", key, " not found. ", numExcluded, " excluded");
std::set<IdentHash> excludedRouters;
for (int i = 0; i < numExcluded; i++)
{
excludedRouters.insert (excluded);
excluded += 32;
}
std::vector<IdentHash> routers;
for (int i = 0; i < 3; i++)
{
auto floodfill = GetClosestFloodfill (buf, excludedRouters);
if (floodfill)
{
routers.push_back (floodfill->GetIdentHash ());
excludedRouters.insert (floodfill->GetIdentHash ());
}
}
replyMsg = CreateDatabaseSearchReply (buf, routers);
}
}
if (!replyMsg)
{
LogPrint ("Requested ", key, " not found. ", numExcluded, " excluded");
std::set<IdentHash> excludedRouters;
for (int i = 0; i < numExcluded; i++)
{
// TODO: check for all zeroes (exploratory)
excludedRouters.insert (excluded);
excluded += 32;
}
replyMsg = CreateDatabaseSearchReply (buf, GetClosestFloodfill (buf, excludedRouters).get ());
}
else
excluded += numExcluded*32; // we don't care about exluded
if (replyMsg)
{
if (replyTunnelID)
{
// encryption might be used though tunnel only
if (flag & 0x02) // encrypted reply requested
if (flag & DATABASE_LOOKUP_ENCYPTION_FLAG) // encrypted reply requested
{
uint8_t * sessionKey = excluded;
uint8_t numTags = sessionKey[32];
@@ -678,8 +782,8 @@ namespace data
{
// new requests
auto exploratoryPool = i2p::tunnel::tunnels.GetExploratoryPool ();
auto outbound = exploratoryPool ? exploratoryPool->GetNextOutboundTunnel () : i2p::tunnel::tunnels.GetNextOutboundTunnel ();
auto inbound = exploratoryPool ? exploratoryPool->GetNextInboundTunnel () : i2p::tunnel::tunnels.GetNextInboundTunnel ();
auto outbound = exploratoryPool ? exploratoryPool->GetNextOutboundTunnel () : nullptr;
auto inbound = exploratoryPool ? exploratoryPool->GetNextInboundTunnel () : nullptr;
bool throughTunnels = outbound && inbound;
CryptoPP::RandomNumberGenerator& rnd = i2p::context.GetRandomNumberGenerator ();
@@ -690,7 +794,16 @@ namespace data
for (int i = 0; i < numDestinations; i++)
{
rnd.GenerateBlock (randomHash, 32);
RequestedDestination * dest = CreateRequestedDestination (IdentHash (randomHash), true);
auto dest = new RequestedDestination (randomHash, true); // exploratory
{
std::unique_lock<std::mutex> l(m_RequestedDestinationsMutex);
if (!m_RequestedDestinations.insert (std::make_pair (randomHash,
std::unique_ptr<RequestedDestination> (dest))).second) // not inserted
{
LogPrint (eLogWarning, "Exploratory destination is requested already");
return;
}
}
auto floodfill = GetClosestFloodfill (randomHash, dest->GetExcludedPeers ());
if (floodfill && !floodfills.count (floodfill.get ())) // request floodfill only once
{
@@ -714,7 +827,10 @@ namespace data
i2p::transport::transports.SendMessage (floodfill->GetIdentHash (), dest->CreateRequestMessage (floodfill->GetIdentHash ()));
}
else
DeleteRequestedDestination (dest);
{
std::unique_lock<std::mutex> l(m_RequestedDestinationsMutex);
m_RequestedDestinations.erase (dest->GetDestination ());
}
}
if (throughTunnels && msgs.size () > 0)
outbound->SendTunnelDataMsg (msgs);
@@ -723,54 +839,18 @@ namespace data
void NetDb::Publish ()
{
std::set<IdentHash> excluded; // TODO: fill up later
for (int i = 0; i < 3; i++)
for (int i = 0; i < 2; i++)
{
auto floodfill = GetClosestFloodfill (i2p::context.GetRouterInfo ().GetIdentHash (), excluded);
if (floodfill)
{
LogPrint ("Publishing our RouterInfo to ", floodfill->GetIdentHashAbbreviation ());
transports.SendMessage (floodfill->GetIdentHash (), CreateDatabaseStoreMsg ());
uint32_t replyToken = i2p::context.GetRandomNumberGenerator ().GenerateWord32 ();
LogPrint ("Publishing our RouterInfo to ", floodfill->GetIdentHashAbbreviation (), ". reply token=", replyToken);
transports.SendMessage (floodfill->GetIdentHash (), CreateDatabaseStoreMsg ((RouterInfo *)nullptr, replyToken));
excluded.insert (floodfill->GetIdentHash ());
}
}
}
RequestedDestination * NetDb::CreateRequestedDestination (const IdentHash& dest, bool isExploratory)
{
std::unique_lock<std::mutex> l(m_RequestedDestinationsMutex);
auto it = m_RequestedDestinations.find (dest);
if (it == m_RequestedDestinations.end ()) // not exist yet
{
RequestedDestination * d = new RequestedDestination (dest, isExploratory);
m_RequestedDestinations[dest] = d;
return d;
}
else
return it->second;
}
bool NetDb::DeleteRequestedDestination (const IdentHash& dest)
{
auto it = m_RequestedDestinations.find (dest);
if (it != m_RequestedDestinations.end ())
{
std::unique_lock<std::mutex> l(m_RequestedDestinationsMutex);
delete it->second;
m_RequestedDestinations.erase (it);
return true;
}
return false;
}
void NetDb::DeleteRequestedDestination (RequestedDestination * dest)
{
if (dest)
{
std::unique_lock<std::mutex> l(m_RequestedDestinationsMutex);
m_RequestedDestinations.erase (dest->GetDestination ());
delete dest;
}
}
}
std::shared_ptr<const RouterInfo> NetDb::GetRandomRouter () const
{
@@ -854,14 +934,36 @@ namespace data
return r;
}
std::shared_ptr<const RouterInfo> NetDb::GetClosestNonFloodfill (const IdentHash& destination,
const std::set<IdentHash>& excluded) const
{
std::shared_ptr<const RouterInfo> r;
XORMetric minMetric;
IdentHash destKey = CreateRoutingKey (destination);
minMetric.SetMax ();
// must be called from NetDb thread only
for (auto it: m_RouterInfos)
{
if (!it.second->IsFloodfill () && !excluded.count (it.first))
{
XORMetric m = destKey ^ it.first;
if (m < minMetric)
{
minMetric = m;
r = it.second;
}
}
}
return r;
}
void NetDb::ManageLeaseSets ()
{
for (auto it = m_LeaseSets.begin (); it != m_LeaseSets.end ();)
{
if (it->second->HasNonExpiredLeases ()) // all leases expired
if (!it->second->HasNonExpiredLeases ()) // all leases expired
{
LogPrint ("LeaseSet ", it->second->GetIdentHash ().ToBase64 (), " expired");
delete it->second;
it = m_LeaseSets.erase (it);
}
else
@@ -872,16 +974,17 @@ namespace data
void NetDb::ManageRequests ()
{
uint64_t ts = i2p::util::GetSecondsSinceEpoch ();
std::unique_lock<std::mutex> l(m_RequestedDestinationsMutex);
for (auto it = m_RequestedDestinations.begin (); it != m_RequestedDestinations.end ();)
{
auto dest = it->second;
auto& dest = it->second;
bool done = false;
if (!dest->IsExploratory () && ts < dest->GetCreationTime () + 60) // request is worthless after 1 minute
if (ts < dest->GetCreationTime () + 60) // request is worthless after 1 minute
{
if (ts > dest->GetCreationTime () + 5) // no response for 5 seconds
{
auto count = dest->GetExcludedPeers ().size ();
if (count < 7)
if (!dest->IsExploratory () && count < 7)
{
auto pool = i2p::tunnel::tunnels.GetExploratoryPool ();
auto outbound = pool->GetNextOutboundTunnel ();
@@ -900,19 +1003,17 @@ namespace data
}
else
{
LogPrint (eLogWarning, dest->GetDestination ().ToBase64 (), " not found after 7 attempts");
if (!dest->IsExploratory ())
LogPrint (eLogWarning, dest->GetDestination ().ToBase64 (), " not found after 7 attempts");
done = true;
}
}
}
else // delete previous exploratory
else // delete obsolete request
done = true;
if (done)
{
delete it->second;
it = m_RequestedDestinations.erase (it);
}
else
it++;
}

39
NetDb.h
View File

@@ -15,36 +15,44 @@
#include "LeaseSet.h"
#include "Tunnel.h"
#include "TunnelPool.h"
#include "Reseed.h"
namespace i2p
{
namespace data
{
class RequestedDestination
{
{
public:
typedef std::function<void (std::shared_ptr<RouterInfo>)> RequestComplete;
RequestedDestination (const IdentHash& destination, bool isExploratory = false):
m_Destination (destination), m_IsExploratory (isExploratory), m_CreationTime (0) {};
~RequestedDestination () { if (m_RequestComplete) m_RequestComplete (nullptr); };
const IdentHash& GetDestination () const { return m_Destination; };
int GetNumExcludedPeers () const { return m_ExcludedPeers.size (); };
const std::set<IdentHash>& GetExcludedPeers () { return m_ExcludedPeers; };
void ClearExcludedPeers ();
std::shared_ptr<const RouterInfo> GetLastRouter () const { return m_LastRouter; };
bool IsExploratory () const { return m_IsExploratory; };
bool IsExcluded (const IdentHash& ident) const { return m_ExcludedPeers.count (ident); };
uint64_t GetCreationTime () const { return m_CreationTime; };
I2NPMessage * CreateRequestMessage (std::shared_ptr<const RouterInfo>, const i2p::tunnel::InboundTunnel * replyTunnel);
I2NPMessage * CreateRequestMessage (std::shared_ptr<const RouterInfo>, std::shared_ptr<const i2p::tunnel::InboundTunnel> replyTunnel);
I2NPMessage * CreateRequestMessage (const IdentHash& floodfill);
void SetRequestComplete (const RequestComplete& requestComplete) { m_RequestComplete = requestComplete; };
bool IsRequestComplete () const { return m_RequestComplete != nullptr; };
void Success (std::shared_ptr<RouterInfo> r);
void Fail ();
private:
IdentHash m_Destination;
bool m_IsExploratory;
std::set<IdentHash> m_ExcludedPeers;
std::shared_ptr<const RouterInfo> m_LastRouter;
uint64_t m_CreationTime;
RequestComplete m_RequestComplete;
};
class NetDb
@@ -59,11 +67,11 @@ namespace data
void AddRouterInfo (const uint8_t * buf, int len);
void AddRouterInfo (const IdentHash& ident, const uint8_t * buf, int len);
void AddLeaseSet (const IdentHash& ident, const uint8_t * buf, int len, i2p::tunnel::InboundTunnel * from);
void AddLeaseSet (const IdentHash& ident, const uint8_t * buf, int len, std::shared_ptr<i2p::tunnel::InboundTunnel> from);
std::shared_ptr<RouterInfo> FindRouter (const IdentHash& ident) const;
LeaseSet * FindLeaseSet (const IdentHash& destination) const;
std::shared_ptr<LeaseSet> FindLeaseSet (const IdentHash& destination) const;
void RequestDestination (const IdentHash& destination);
void RequestDestination (const IdentHash& destination, RequestedDestination::RequestComplete requestComplete = nullptr);
void HandleDatabaseStoreMsg (I2NPMessage * msg);
void HandleDatabaseSearchReplyMsg (I2NPMessage * msg);
@@ -73,10 +81,13 @@ namespace data
std::shared_ptr<const RouterInfo> GetRandomRouter (std::shared_ptr<const RouterInfo> compatibleWith) const;
std::shared_ptr<const RouterInfo> GetHighBandwidthRandomRouter (std::shared_ptr<const RouterInfo> compatibleWith) const;
std::shared_ptr<const RouterInfo> GetClosestFloodfill (const IdentHash& destination, const std::set<IdentHash>& excluded) const;
std::shared_ptr<const RouterInfo> GetClosestNonFloodfill (const IdentHash& destination, const std::set<IdentHash>& excluded) const;
void SetUnreachable (const IdentHash& ident, bool unreachable);
void PostI2NPMsg (I2NPMessage * msg);
void Reseed ();
// for web interface
int GetNumRouters () const { return m_RouterInfos.size (); };
int GetNumFloodfills () const { return m_Floodfills.size (); };
@@ -93,27 +104,25 @@ namespace data
void ManageLeaseSets ();
void ManageRequests ();
RequestedDestination * CreateRequestedDestination (const IdentHash& dest, bool isExploratory = false);
bool DeleteRequestedDestination (const IdentHash& dest); // returns true if found
void DeleteRequestedDestination (RequestedDestination * dest);
template<typename Filter>
std::shared_ptr<const RouterInfo> GetRandomRouter (Filter filter) const;
private:
std::map<IdentHash, LeaseSet *> m_LeaseSets;
std::map<IdentHash, std::shared_ptr<LeaseSet> > m_LeaseSets;
mutable std::mutex m_RouterInfosMutex;
std::map<IdentHash, std::shared_ptr<RouterInfo> > m_RouterInfos;
mutable std::mutex m_FloodfillsMutex;
std::list<std::shared_ptr<RouterInfo> > m_Floodfills;
std::mutex m_RequestedDestinationsMutex;
std::map<IdentHash, RequestedDestination *> m_RequestedDestinations;
std::map<IdentHash, std::unique_ptr<RequestedDestination> > m_RequestedDestinations;
bool m_IsRunning;
std::thread * m_Thread;
i2p::util::Queue<I2NPMessage> m_Queue; // of I2NPDatabaseStoreMsg
Reseeder * m_Reseeder;
static const char m_NetDbPath[];
};

20
Queue.h
View File

@@ -2,6 +2,7 @@
#define QUEUE_H__
#include <queue>
#include <vector>
#include <mutex>
#include <thread>
#include <condition_variable>
@@ -23,6 +24,17 @@ namespace util
m_NonEmpty.notify_one ();
}
void Put (const std::vector<Element *>& vec)
{
if (!vec.empty ())
{
std::unique_lock<std::mutex> l(m_QueueMutex);
for (auto it: vec)
m_Queue.push (it);
m_NonEmpty.notify_one ();
}
}
Element * GetNext ()
{
std::unique_lock<std::mutex> l(m_QueueMutex);
@@ -64,7 +76,13 @@ namespace util
std::unique_lock<std::mutex> l(m_QueueMutex);
return m_Queue.empty ();
}
int GetSize ()
{
std::unique_lock<std::mutex> l(m_QueueMutex);
return m_Queue.size ();
}
void WakeUp () { m_NonEmpty.notify_all (); };
Element * Get ()

View File

@@ -3,6 +3,12 @@ i2pd
I2P router written in C++
License
-------
This project is licensed under the BSD 3-clause license, which can be found in the file
LICENSE in the root of the project source code.
Requirements for Linux/FreeBSD/OSX
----------------------------------
@@ -16,10 +22,17 @@ VS2013 (known to work with 12.0.21005.1 or newer), Boost 1.46 or newer,
crypto++ 5.62. See Win32/README-Build.txt for instructions on how to build i2pd
and its dependencies.
Downloads
------------
Official binary releases could be found at:
http://download.i2p.io/purplei2p/i2pd/releases/
Build Statuses
---------------
- Linux x64 - [![Build Status](https://jenkins.nordcloud.no/buildStatus/icon?job=i2pd-linux)](https://jenkins.nordcloud.no/job/i2pd-linux/)
- Linux x64 - [![Build Status](https://jenkins.greyhat.no/buildStatus/icon?job=i2pd-linux)](https://jenkins.nordcloud.no/job/i2pd-linux/)
- Linux ARM - To be added
- Mac OS X - Got it working, but not well tested. (Only works with clang, not GCC.)
- Microsoft VC13 - To be added
@@ -30,6 +43,8 @@ Testing
First, build it.
On Ubuntu/Debian based
* sudo apt-get install libboost-dev libboost-filesystem-dev libboost-program-options-dev libboost-regex-dev libcrypto++-dev libboost-date-time-dev
* $ cd i2pd
* $ make
@@ -59,6 +74,7 @@ Cmdline options
* --service= - 1 if uses system folders (/var/run/i2pd.pid, /var/log/i2pd.log, /var/lib/i2pd).
* --unreachable= - 1 if router is declared as unreachable and works through introducers.
* --v6= - 1 if supports communication through ipv6, off by default
* --floodfill= - 1 if router is floodfill, off by default
* --httpproxyport= - The port to listen on (HTTP Proxy)
* --socksproxyport= - The port to listen on (SOCKS Proxy)
* --ircport= - The local port of IRC tunnel to listen on. 6668 by default
@@ -70,6 +86,7 @@ Cmdline options
* --eepport= - Port incoming trafic forward to. 80 by default
* --samport= - Port of SAM bridge. Usually 7656. SAM is off if not specified
* --bobport= - Port of BOB command channel. Usually 2827. BOB is off if not specified
* --i2pcontrolport= - Port of I2P control service. Usually 7650. I2PControl is off if not specified
* --conf= - Config file (default: ~/.i2pd/i2p.conf or /var/lib/i2pd/i2p.conf)
This parameter will be silently ignored if the specified config file does not exist.
Options specified on the command line take precedence over those in the config file.

View File

@@ -3,10 +3,11 @@
#include <sstream>
#include <boost/regex.hpp>
#include <boost/filesystem.hpp>
#include <cryptopp/osrng.h>
#include <boost/lexical_cast.hpp>
#include <cryptopp/asn.h>
#include <cryptopp/base64.h>
#include <cryptopp/crc.h>
#include <cryptopp/hmac.h>
#include <cryptopp/zinflate.h>
#include "I2PEndian.h"
#include "Reseed.h"
@@ -23,34 +24,29 @@ namespace data
{
static std::vector<std::string> httpReseedHostList = {
"http://193.150.121.66/netDb/",
"http://netdb.i2p2.no/",
"http://reseed.i2p-projekt.de/",
"http://cowpuncher.drollette.com/netdb/",
// "http://193.150.121.66/netDb/", // unstable
// "http://us.reseed.i2p2.no/", // misconfigured, not serving reseed data
// "http://jp.reseed.i2p2.no/", // Really outdated RIs
"http://netdb.i2p2.no/", // only SU3 (v2) support
"http://i2p.mooo.com/netDb/",
"http://reseed.info/",
"http://uk.reseed.i2p2.no/",
"http://us.reseed.i2p2.no/",
"http://jp.reseed.i2p2.no/",
"http://i2p-netdb.innovatio.no/",
"http://ieb9oopo.mooo.com"
"http://i2p-netdb.innovatio.no/"
};
//TODO: Remember to add custom port support. Not all serves on 443
static std::vector<std::string> httpsReseedHostList = {
"https://193.150.121.66/netDb/",
"https://netdb.i2p2.no/",
"https://reseed.i2p-projekt.de/",
"https://cowpuncher.drollette.com/netdb/",
"https://i2p.mooo.com/netDb/",
"https://reseed.info/",
"https://i2p-netdb.innovatio.no/",
"https://ieb9oopo.mooo.com/",
"https://ssl.webpack.de/ivae2he9.sg4.e-plaza.de/" // Only HTTPS and SU3 (v2) support
// "https://193.150.121.66/netDb/", // unstable
// "https://i2p-netdb.innovatio.no/",// Vuln to POODLE
"https://netdb.i2p2.no/", // Only SU3 (v2) support
"https://reseed.i2p-projekt.de/", // Only HTTPS
"https://cowpuncher.drollette.com/netdb/", // Only HTTPS and SU3 (v2) support -- will move to a new location
// following hosts are fine but don't support AES256
/*"https://i2p.mooo.com/netDb/",
"https://link.mx24.eu/", // Only HTTPS and SU3 (v2) support
"https://i2pseed.zarrenspry.info/", // Only HTTPS and SU3 (v2) support
"https://ieb9oopo.mooo.com/" // Only HTTPS and SU3 (v2) support*/
};
//TODO: Implement v2 reseeding. Lightweight zip library is needed.
//TODO: Implement SU3, utils.
Reseeder::Reseeder()
{
}
@@ -61,17 +57,9 @@ namespace data
bool Reseeder::reseedNow()
{
// This method is deprecated
try
{
// Seems like the best place to try to intercept with SSL
/*ssl_server = true;
try {
// SSL
}
catch (std::exception& e)
{
LogPrint("Exception in SSL: ", e.what());
}*/
std::string reseedHost = httpReseedHostList[(rand() % httpReseedHostList.size())];
LogPrint("Reseeding from ", reseedHost);
std::string content = i2p::util::http::httpRequest(reseedHost);
@@ -132,16 +120,17 @@ namespace data
int Reseeder::ReseedNowSU3 ()
{
CryptoPP::AutoSeededRandomPool rnd;
auto ind = rnd.GenerateWord32 (0, httpReseedHostList.size() - 1);
std::string reseedHost = httpReseedHostList[ind];
return ReseedFromSU3 (reseedHost);
auto ind = rnd.GenerateWord32 (0, httpReseedHostList.size() - 1 + httpsReseedHostList.size () - 1);
std::string reseedHost = (ind < httpReseedHostList.size()) ? httpReseedHostList[ind] :
httpsReseedHostList[ind - httpReseedHostList.size()];
return ReseedFromSU3 (reseedHost, ind >= httpReseedHostList.size());
}
int Reseeder::ReseedFromSU3 (const std::string& host)
int Reseeder::ReseedFromSU3 (const std::string& host, bool https)
{
std::string url = host + "i2pseeds.su3";
LogPrint (eLogInfo, "Dowloading SU3 from ", host);
std::string su3 = i2p::util::http::httpRequest (url);
std::string su3 = https ? HttpsRequest (url) : i2p::util::http::httpRequest (url);
if (su3.length () > 0)
{
std::stringstream s(su3);
@@ -266,9 +255,9 @@ namespace data
s.read ((char *)&compressionMethod, 2);
compressionMethod = le16toh (compressionMethod);
s.seekg (4, std::ios::cur); // skip fields we don't care about
uint32_t crc32, compressedSize, uncompressedSize;
s.read ((char *)&crc32, 4);
crc32 = le32toh (crc32);
uint32_t compressedSize, uncompressedSize;
uint8_t crc32[4];
s.read ((char *)crc32, 4);
s.read ((char *)&compressedSize, 4);
compressedSize = le32toh (compressedSize);
s.read ((char *)&uncompressedSize, 4);
@@ -292,8 +281,7 @@ namespace data
return numFiles;
}
s.read ((char *)&crc32, 4);
crc32 = le32toh (crc32);
s.read ((char *)crc32, 4);
s.read ((char *)&compressedSize, 4);
compressedSize = le32toh (compressedSize) + 4; // ??? we must consider signature as part of compressed data
s.read ((char *)&uncompressedSize, 4);
@@ -321,7 +309,7 @@ namespace data
{
uint8_t * uncompressed = new uint8_t[uncompressedSize];
decompressor.Get (uncompressed, uncompressedSize);
if (CryptoPP::CRC32().VerifyDigest ((uint8_t *)&crc32, uncompressed, uncompressedSize))
if (CryptoPP::CRC32().VerifyDigest (crc32, uncompressed, uncompressedSize))
{
i2p::data::netdb.AddRouterInfo (uncompressed, uncompressedSize);
numFiles++;
@@ -405,75 +393,81 @@ namespace data
decoder.Attach (new CryptoPP::Redirector (queue));
decoder.Put ((const uint8_t *)base64.data(), base64.length());
decoder.MessageEnd ();
// extract X.509
CryptoPP::BERSequenceDecoder x509Cert (queue);
CryptoPP::BERSequenceDecoder tbsCert (x509Cert);
// version
uint32_t ver;
CryptoPP::BERGeneralDecoder context (tbsCert, CryptoPP::CONTEXT_SPECIFIC | CryptoPP::CONSTRUCTED);
CryptoPP::BERDecodeUnsigned<uint32_t>(context, ver, CryptoPP::INTEGER);
// serial
CryptoPP::Integer serial;
serial.BERDecode(tbsCert);
// signature
CryptoPP::BERSequenceDecoder signature (tbsCert);
signature.SkipAll();
// issuer
std::string name;
CryptoPP::BERSequenceDecoder issuer (tbsCert);
{
CryptoPP::BERSetDecoder c (issuer); c.SkipAll();
CryptoPP::BERSetDecoder st (issuer); st.SkipAll();
CryptoPP::BERSetDecoder l (issuer); l.SkipAll();
CryptoPP::BERSetDecoder o (issuer); o.SkipAll();
CryptoPP::BERSetDecoder ou (issuer); ou.SkipAll();
CryptoPP::BERSetDecoder cn (issuer);
{
CryptoPP::BERSequenceDecoder attributes (cn);
{
CryptoPP::BERGeneralDecoder ident(attributes, CryptoPP::OBJECT_IDENTIFIER);
ident.SkipAll ();
CryptoPP::BERDecodeTextString (attributes, name, CryptoPP::UTF8_STRING);
}
}
}
issuer.SkipAll();
// validity
CryptoPP::BERSequenceDecoder validity (tbsCert);
validity.SkipAll();
// subject
CryptoPP::BERSequenceDecoder subject (tbsCert);
subject.SkipAll();
// public key
CryptoPP::BERSequenceDecoder publicKey (tbsCert);
{
CryptoPP::BERSequenceDecoder ident (publicKey);
ident.SkipAll ();
CryptoPP::BERGeneralDecoder key (publicKey, CryptoPP::BIT_STRING);
key.Skip (1); // FIXME: probably bug in crypto++
CryptoPP::BERSequenceDecoder keyPair (key);
CryptoPP::Integer n;
n.BERDecode (keyPair);
if (name.length () > 0)
{
PublicKey value;
n.Encode (value, 512);
m_SigningKeys[name] = value;
}
else
LogPrint (eLogWarning, "Unknown issuer. Skipped");
}
publicKey.SkipAll();
tbsCert.SkipAll();
x509Cert.SkipAll();
LoadCertificate (queue);
}
else
LogPrint (eLogError, "Can't open certificate file ", filename);
}
std::string Reseeder::LoadCertificate (CryptoPP::ByteQueue& queue)
{
// extract X.509
CryptoPP::BERSequenceDecoder x509Cert (queue);
CryptoPP::BERSequenceDecoder tbsCert (x509Cert);
// version
uint32_t ver;
CryptoPP::BERGeneralDecoder context (tbsCert, CryptoPP::CONTEXT_SPECIFIC | CryptoPP::CONSTRUCTED);
CryptoPP::BERDecodeUnsigned<uint32_t>(context, ver, CryptoPP::INTEGER);
// serial
CryptoPP::Integer serial;
serial.BERDecode(tbsCert);
// signature
CryptoPP::BERSequenceDecoder signature (tbsCert);
signature.SkipAll();
// issuer
std::string name;
CryptoPP::BERSequenceDecoder issuer (tbsCert);
{
CryptoPP::BERSetDecoder c (issuer); c.SkipAll();
CryptoPP::BERSetDecoder st (issuer); st.SkipAll();
CryptoPP::BERSetDecoder l (issuer); l.SkipAll();
CryptoPP::BERSetDecoder o (issuer); o.SkipAll();
CryptoPP::BERSetDecoder ou (issuer); ou.SkipAll();
CryptoPP::BERSetDecoder cn (issuer);
{
CryptoPP::BERSequenceDecoder attributes (cn);
{
CryptoPP::BERGeneralDecoder ident(attributes, CryptoPP::OBJECT_IDENTIFIER);
ident.SkipAll ();
CryptoPP::BERDecodeTextString (attributes, name, CryptoPP::UTF8_STRING);
}
}
}
issuer.SkipAll();
// validity
CryptoPP::BERSequenceDecoder validity (tbsCert);
validity.SkipAll();
// subject
CryptoPP::BERSequenceDecoder subject (tbsCert);
subject.SkipAll();
// public key
CryptoPP::BERSequenceDecoder publicKey (tbsCert);
{
CryptoPP::BERSequenceDecoder ident (publicKey);
ident.SkipAll ();
CryptoPP::BERGeneralDecoder key (publicKey, CryptoPP::BIT_STRING);
key.Skip (1); // FIXME: probably bug in crypto++
CryptoPP::BERSequenceDecoder keyPair (key);
CryptoPP::Integer n;
n.BERDecode (keyPair);
if (name.length () > 0)
{
PublicKey value;
n.Encode (value, 512);
m_SigningKeys[name] = value;
}
else
LogPrint (eLogWarning, "Unknown issuer. Skipped");
}
publicKey.SkipAll();
tbsCert.SkipAll();
x509Cert.SkipAll();
return name;
}
void Reseeder::LoadCertificates ()
{
boost::filesystem::path reseedDir = i2p::util::filesystem::GetCertificatesDir() / "reseed";
@@ -496,7 +490,335 @@ namespace data
}
LogPrint (eLogInfo, numCertificates, " certificates loaded");
}
std::string Reseeder::HttpsRequest (const std::string& address)
{
i2p::util::http::url u(address);
TlsSession session (u.host_, 443);
// send request
std::stringstream ss;
ss << "GET " << u.path_ << " HTTP/1.1\r\nHost: " << u.host_
<< "\r\nAccept: */*\r\n" << "User-Agent: Wget/1.11.4\r\n" << "Connection: close\r\n\r\n";
session.Send ((uint8_t *)ss.str ().c_str (), ss.str ().length ());
// read response
std::stringstream rs;
while (session.Receive (rs))
;
return i2p::util::http::GetHttpContent (rs);
}
TlsSession::TlsSession (const std::string& host, int port):
m_Seqn (0)
{
m_Site.connect(host, boost::lexical_cast<std::string>(port));
if (m_Site.good ())
{
Handshake ();
}
else
LogPrint (eLogError, "Can't connect to ", host, ":", port);
}
void TlsSession::Handshake ()
{
static uint8_t clientHello[] =
{
0x16, // handshake
0x03, 0x03, // version (TLS 1.2)
0x00, 0x2F, // length of handshake
// handshake
0x01, // handshake type (client hello)
0x00, 0x00, 0x2B, // length of handshake payload
// client hello
0x03, 0x03, // highest version supported (TLS 1.2)
0x45, 0xFA, 0x01, 0x19, 0x74, 0x55, 0x18, 0x36,
0x42, 0x05, 0xC1, 0xDD, 0x4A, 0x21, 0x80, 0x80,
0xEC, 0x37, 0x11, 0x93, 0x16, 0xF4, 0x66, 0x00,
0x12, 0x67, 0xAB, 0xBA, 0xFF, 0x29, 0x13, 0x9E, // 32 random bytes
0x00, // session id length
0x00, 0x02, // chiper suites length
0x00, 0x3D, // RSA_WITH_AES_256_CBC_SHA256
0x01, // compression methods length
0x00, // no compression
0x00, 0x00 // extensions length
};
static uint8_t changeCipherSpecs[] =
{
0x14, // change cipher specs
0x03, 0x03, // version (TLS 1.2)
0x00, 0x01, // length
0x01 // type
};
static uint8_t finished[] =
{
0x16, // handshake
0x03, 0x03, // version (TLS 1.2)
0x00, 0x50, // length of handshake (80 bytes)
// handshake (encrypted)
// unencrypted context
// 0x14 handshake type (finished)
// 0x00, 0x00, 0x0C length of handshake payload
// 12 bytes of verified data
};
// send ClientHello
m_Site.write ((char *)clientHello, sizeof (clientHello));
m_FinishedHash.Update (clientHello + 5, sizeof (clientHello) - 5);
// read ServerHello
uint8_t type;
m_Site.read ((char *)&type, 1);
uint16_t version;
m_Site.read ((char *)&version, 2);
uint16_t length;
m_Site.read ((char *)&length, 2);
length = be16toh (length);
char * serverHello = new char[length];
m_Site.read (serverHello, length);
m_FinishedHash.Update ((uint8_t *)serverHello, length);
uint8_t serverRandom[32];
if (serverHello[0] == 0x02) // handshake type server hello
memcpy (serverRandom, serverHello + 6, 32);
else
LogPrint (eLogError, "Unexpected handshake type ", (int)serverHello[0]);
delete[] serverHello;
// read Certificate
m_Site.read ((char *)&type, 1);
m_Site.read ((char *)&version, 2);
m_Site.read ((char *)&length, 2);
length = be16toh (length);
char * certificate = new char[length];
m_Site.read (certificate, length);
m_FinishedHash.Update ((uint8_t *)certificate, length);
CryptoPP::RSA::PublicKey publicKey;
// 0 - handshake type
// 1 - 3 - handshake payload length
// 4 - 6 - length of array of certificates
// 7 - 9 - length of certificate
if (certificate[0] == 0x0B) // handshake type certificate
publicKey = ExtractPublicKey ((uint8_t *)certificate + 10, length - 10);
else
LogPrint (eLogError, "Unexpected handshake type ", (int)certificate[0]);
delete[] certificate;
// read ServerHelloDone
m_Site.read ((char *)&type, 1);
m_Site.read ((char *)&version, 2);
m_Site.read ((char *)&length, 2);
length = be16toh (length);
char * serverHelloDone = new char[length];
m_Site.read (serverHelloDone, length);
m_FinishedHash.Update ((uint8_t *)serverHelloDone, length);
if (serverHelloDone[0] != 0x0E) // handshake type hello done
LogPrint (eLogError, "Unexpected handshake type ", (int)serverHelloDone[0]);
delete[] serverHelloDone;
// our turn now
// generate secret key
uint8_t secret[48];
secret[0] = 3; secret[1] = 3; // version
m_Rnd.GenerateBlock (secret + 2, 46); // 46 random bytes
// encrypt RSA
CryptoPP::RSAES_PKCS1v15_Encryptor encryptor(publicKey);
size_t encryptedLen = encryptor.CiphertextLength (48); // number of bytes for encrypted 48 bytes, usually 256 (2048 bits key)
uint8_t * encrypted = new uint8_t[encryptedLen + 2]; // + 2 bytes for length
htobe16buf (encrypted, encryptedLen); // first two bytes means length
encryptor.Encrypt (m_Rnd, secret, 48, encrypted + 2);
// send ClientKeyExchange
// 0x10 - handshake type "client key exchange"
SendHandshakeMsg (0x10, encrypted, encryptedLen + 2);
delete[] encrypted;
// send ChangeCipherSpecs
m_Site.write ((char *)changeCipherSpecs, sizeof (changeCipherSpecs));
// calculate master secret
uint8_t masterSecret[48], random[64];
memcpy (random, clientHello + 11, 32);
memcpy (random + 32, serverRandom, 32);
PRF (secret, "master secret", random, 64, 48, masterSecret);
// expand master secret
uint8_t keys[128]; // clientMACKey(32), serverMACKey(32), clientKey(32), serverKey(32)
memcpy (random, serverRandom, 32);
memcpy (random + 32, clientHello + 11, 32);
PRF (masterSecret, "key expansion", random, 64, 128, keys);
memcpy (m_MacKey, keys, 32);
m_Encryption.SetKey (keys + 64);
m_Decryption.SetKey (keys + 96);
// send finished
uint8_t finishedHashDigest[32], finishedPayload[40], encryptedPayload[80];
finishedPayload[0] = 0x14; // handshake type (finished)
finishedPayload[1] = 0; finishedPayload[2] = 0; finishedPayload[3] = 0x0C; // 12 bytes
m_FinishedHash.Final (finishedHashDigest);
PRF (masterSecret, "client finished", finishedHashDigest, 32, 12, finishedPayload + 4);
uint8_t mac[32];
CalculateMAC (0x16, finishedPayload, 16, mac);
Encrypt (finishedPayload, 16, mac, encryptedPayload);
m_Site.write ((char *)finished, sizeof (finished));
m_Site.write ((char *)encryptedPayload, 80);
// read ChangeCipherSpecs
uint8_t changeCipherSpecs1[6];
m_Site.read ((char *)changeCipherSpecs1, 6);
// read finished
m_Site.read ((char *)&type, 1);
m_Site.read ((char *)&version, 2);
m_Site.read ((char *)&length, 2);
length = be16toh (length);
char * finished1 = new char[length];
m_Site.read (finished1, length);
delete[] finished1;
}
void TlsSession::SendHandshakeMsg (uint8_t handshakeType, uint8_t * data, size_t len)
{
uint8_t handshakeHeader[9];
handshakeHeader[0] = 0x16; // handshake
handshakeHeader[1] = 0x03; handshakeHeader[2] = 0x03; // version is always TLS 1.2 (3,3)
htobe16buf (handshakeHeader + 3, len + 4); // length of payload
//payload starts
handshakeHeader[5] = handshakeType; // handshake type
handshakeHeader[6] = 0; // highest byte of payload length is always zero
htobe16buf (handshakeHeader + 7, len); // length of data
m_Site.write ((char *)handshakeHeader, 9);
m_FinishedHash.Update (handshakeHeader + 5, 4); // only payload counts
m_Site.write ((char *)data, len);
m_FinishedHash.Update (data, len);
}
void TlsSession::PRF (const uint8_t * secret, const char * label, const uint8_t * random, size_t randomLen,
size_t len, uint8_t * buf)
{
// secret is assumed 48 bytes
// random is not more than 64 bytes
CryptoPP::HMAC<CryptoPP::SHA256> hmac (secret, 48);
uint8_t seed[96]; size_t seedLen;
seedLen = strlen (label);
memcpy (seed, label, seedLen);
memcpy (seed + seedLen, random, randomLen);
seedLen += randomLen;
size_t offset = 0;
uint8_t a[128];
hmac.CalculateDigest (a, seed, seedLen);
while (offset < len)
{
memcpy (a + 32, seed, seedLen);
hmac.CalculateDigest (buf + offset, a, seedLen + 32);
offset += 32;
hmac.CalculateDigest (a, a, 32);
}
}
size_t TlsSession::Encrypt (const uint8_t * in, size_t len, const uint8_t * mac, uint8_t * out)
{
size_t size = 0;
m_Rnd.GenerateBlock (out, 16); // iv
size += 16;
m_Encryption.SetIV (out);
memcpy (out + size, in, len);
size += len;
memcpy (out + size, mac, 32);
size += 32;
uint8_t paddingSize = len + 1;
paddingSize &= 0x0F; // %16
if (paddingSize > 0) paddingSize = 16 - paddingSize;
memset (out + size, paddingSize, paddingSize + 1); // paddind and last byte are equal to padding size
size += paddingSize + 1;
m_Encryption.Encrypt (out + 16, size - 16, out + 16);
return size;
}
size_t TlsSession::Decrypt (uint8_t * buf, size_t len)
{
m_Decryption.SetIV (buf);
m_Decryption.Decrypt (buf + 16, len - 16, buf + 16);
return len - 48 - buf[len -1] - 1; // IV(16), mac(32) and padding
}
void TlsSession::CalculateMAC (uint8_t type, const uint8_t * buf, size_t len, uint8_t * mac)
{
uint8_t header[13]; // seqn (8) + type (1) + version (2) + length (2)
htobe64buf (header, m_Seqn);
header[8] = type; header[9] = 3; header[10] = 3; // 3,3 means TLS 1.2
htobe16buf (header + 11, len);
CryptoPP::HMAC<CryptoPP::SHA256> hmac (m_MacKey, 32);
hmac.Update (header, 13);
hmac.Update (buf, len);
hmac.Final (mac);
m_Seqn++;
}
CryptoPP::RSA::PublicKey TlsSession::ExtractPublicKey (const uint8_t * certificate, size_t len)
{
CryptoPP::ByteQueue queue;
queue.Put (certificate, len);
queue.MessageEnd ();
// extract X.509
CryptoPP::BERSequenceDecoder x509Cert (queue);
CryptoPP::BERSequenceDecoder tbsCert (x509Cert);
// version
uint32_t ver;
CryptoPP::BERGeneralDecoder context (tbsCert, CryptoPP::CONTEXT_SPECIFIC | CryptoPP::CONSTRUCTED);
CryptoPP::BERDecodeUnsigned<uint32_t>(context, ver, CryptoPP::INTEGER);
// serial
CryptoPP::Integer serial;
serial.BERDecode(tbsCert);
// signature
CryptoPP::BERSequenceDecoder signature (tbsCert);
signature.SkipAll();
// issuer
CryptoPP::BERSequenceDecoder issuer (tbsCert);
issuer.SkipAll();
// validity
CryptoPP::BERSequenceDecoder validity (tbsCert);
validity.SkipAll();
// subject
CryptoPP::BERSequenceDecoder subject (tbsCert);
subject.SkipAll();
// public key
CryptoPP::BERSequenceDecoder publicKey (tbsCert);
CryptoPP::BERSequenceDecoder ident (publicKey);
ident.SkipAll ();
CryptoPP::BERGeneralDecoder key (publicKey, CryptoPP::BIT_STRING);
key.Skip (1); // FIXME: probably bug in crypto++
CryptoPP::BERSequenceDecoder keyPair (key);
CryptoPP::Integer n, e;
n.BERDecode (keyPair);
e.BERDecode (keyPair);
CryptoPP::RSA::PublicKey ret;
ret.Initialize (n, e);
return ret;
}
void TlsSession::Send (const uint8_t * buf, size_t len)
{
uint8_t * out = new uint8_t[len + 64 + 5]; // 64 = 32 mac + 16 iv + upto 16 padding, 5 = header
out[0] = 0x17; // application data
out[1] = 0x03; out[2] = 0x03; // version
uint8_t mac[32];
CalculateMAC (0x17, buf, len, mac);
size_t encryptedLen = Encrypt (buf, len, mac, out + 5);
htobe16buf (out + 3, encryptedLen);
m_Site.write ((char *)out, encryptedLen + 5);
delete[] out;
}
bool TlsSession::Receive (std::ostream& rs)
{
if (m_Site.eof ()) return false;
uint8_t type; uint16_t version, length;
m_Site.read ((char *)&type, 1);
m_Site.read ((char *)&version, 2);
m_Site.read ((char *)&length, 2);
length = be16toh (length);
uint8_t * buf = new uint8_t[length];
m_Site.read ((char *)buf, length);
size_t decryptedLen = Decrypt (buf, length);
rs.write ((char *)buf + 16, decryptedLen);
delete[] buf;
return true;
}
}
}

View File

@@ -5,7 +5,11 @@
#include <string>
#include <vector>
#include <map>
#include <cryptopp/osrng.h>
#include <cryptopp/rsa.h>
#include <boost/asio.hpp>
#include "Identity.h"
#include "aes.h"
namespace i2p
{
@@ -24,21 +28,54 @@ namespace data
int ReseedNowSU3 ();
void LoadCertificates ();
private:
void LoadCertificate (const std::string& filename);
std::string LoadCertificate (CryptoPP::ByteQueue& queue); // returns issuer's name
int ReseedFromSU3 (const std::string& host);
int ReseedFromSU3 (const std::string& host, bool https = false);
int ProcessSU3File (const char * filename);
int ProcessSU3Stream (std::istream& s);
bool FindZipDataDescriptor (std::istream& s);
std::string HttpsRequest (const std::string& address);
private:
std::map<std::string, PublicKey> m_SigningKeys;
};
class TlsSession
{
public:
TlsSession (const std::string& host, int port);
void Send (const uint8_t * buf, size_t len);
bool Receive (std::ostream& rs);
private:
void Handshake ();
void SendHandshakeMsg (uint8_t handshakeType, uint8_t * data, size_t len);
CryptoPP::RSA::PublicKey ExtractPublicKey (const uint8_t * certificate, size_t len);
void PRF (const uint8_t * secret, const char * label, const uint8_t * random, size_t randomLen,
size_t len, uint8_t * buf);
void CalculateMAC (uint8_t type, const uint8_t * buf, size_t len, uint8_t * mac);
size_t Encrypt (const uint8_t * in, size_t len, const uint8_t * mac, uint8_t * out);
size_t Decrypt (uint8_t * buf, size_t len); // pyaload is buf + 16
private:
uint64_t m_Seqn;
boost::asio::ip::tcp::iostream m_Site;
CryptoPP::SHA256 m_FinishedHash;
CryptoPP::AutoSeededRandomPool m_Rnd;
i2p::crypto::CBCEncryption m_Encryption;
i2p::crypto::CBCDecryption m_Decryption;
uint8_t m_MacKey[32]; // client
};
}
}

View File

@@ -13,7 +13,8 @@ namespace i2p
RouterContext context;
RouterContext::RouterContext ():
m_LastUpdateTime (0), m_IsUnreachable (false), m_AcceptsTunnels (true)
m_LastUpdateTime (0), m_IsUnreachable (false), m_AcceptsTunnels (true),
m_IsFloodfill (false)
{
}
@@ -107,6 +108,16 @@ namespace i2p
UpdateRouterInfo ();
}
void RouterContext::SetFloodfill (bool floodfill)
{
m_IsFloodfill = floodfill;
if (floodfill)
m_RouterInfo.SetCaps (m_RouterInfo.GetCaps () | i2p::data::RouterInfo::eFloodfill);
else
m_RouterInfo.SetCaps (m_RouterInfo.GetCaps () & ~i2p::data::RouterInfo::eFloodfill);
UpdateRouterInfo ();
}
void RouterContext::SetUnreachable ()
{
m_IsUnreachable = true;
@@ -204,7 +215,7 @@ namespace i2p
fk.write ((char *)&keys, sizeof (keys));
}
void RouterContext::HandleI2NPMessage (const uint8_t * buf, size_t len, i2p::tunnel::InboundTunnel * from)
void RouterContext::HandleI2NPMessage (const uint8_t * buf, size_t len, std::shared_ptr<i2p::tunnel::InboundTunnel> from)
{
i2p::HandleI2NPMessage (CreateI2NPMessage (buf, GetI2NPMessageLength (buf), from));
}

View File

@@ -37,7 +37,9 @@ namespace i2p
bool AddIntroducer (const i2p::data::RouterInfo& routerInfo, uint32_t tag);
void RemoveIntroducer (const boost::asio::ip::udp::endpoint& e);
bool IsUnreachable () const { return m_IsUnreachable; };
void SetUnreachable ();
void SetUnreachable ();
bool IsFloodfill () const { return m_IsFloodfill; };
void SetFloodfill (bool floodfill);
bool AcceptsTunnels () const { return m_AcceptsTunnels; };
void SetAcceptsTunnels (bool acceptsTunnels) { m_AcceptsTunnels = acceptsTunnels; };
bool SupportsV6 () const { return m_RouterInfo.IsV6 (); };
@@ -52,7 +54,7 @@ namespace i2p
// implements GarlicDestination
const i2p::data::LeaseSet * GetLeaseSet () { return nullptr; };
void HandleI2NPMessage (const uint8_t * buf, size_t len, i2p::tunnel::InboundTunnel * from);
void HandleI2NPMessage (const uint8_t * buf, size_t len, std::shared_ptr<i2p::tunnel::InboundTunnel> from);
private:
@@ -68,7 +70,7 @@ namespace i2p
i2p::data::PrivateKeys m_Keys;
CryptoPP::AutoSeededRandomPool m_Rnd;
uint64_t m_LastUpdateTime;
bool m_IsUnreachable, m_AcceptsTunnels;
bool m_IsUnreachable, m_AcceptsTunnels, m_IsFloodfill;
};
extern RouterContext context;

View File

@@ -150,9 +150,17 @@ namespace data
address.host = boost::asio::ip::address::from_string (value, ecode);
if (ecode)
{
// TODO: we should try to resolve address here
LogPrint (eLogWarning, "Unexpected address ", value);
isValidAddress = false;
if (address.transportStyle == eTransportNTCP)
{
m_SupportedTransports |= eNTCPV4; // TODO:
address.addressString = value;
}
else
{
// TODO: resolve address for SSU
LogPrint (eLogWarning, "Unexpected SSU address ", value);
isValidAddress = false;
}
}
else
{
@@ -268,8 +276,13 @@ namespace data
void RouterInfo::UpdateCapsProperty ()
{
std::string caps;
caps += (m_Caps & eHighBandwidth) ? CAPS_FLAG_HIGH_BANDWIDTH1 : CAPS_FLAG_LOW_BANDWIDTH2; // bandwidth
if (m_Caps & eFloodfill) caps += CAPS_FLAG_FLOODFILL; // floodfill
if (m_Caps & eFloodfill)
{
caps += CAPS_FLAG_HIGH_BANDWIDTH3; // highest bandwidth
caps += CAPS_FLAG_FLOODFILL; // floodfill
}
else
caps += (m_Caps & eHighBandwidth) ? CAPS_FLAG_HIGH_BANDWIDTH1 : CAPS_FLAG_LOW_BANDWIDTH2; // bandwidth
if (m_Caps & eHidden) caps += CAPS_FLAG_HIDDEN; // hidden
if (m_Caps & eReachable) caps += CAPS_FLAG_REACHABLE; // reachable
if (m_Caps & eUnreachable) caps += CAPS_FLAG_UNREACHABLE; // unreachable

View File

@@ -69,6 +69,7 @@ namespace data
{
TransportStyle transportStyle;
boost::asio::ip::address host;
std::string addressString;
int port, mtu;
uint64_t date;
uint8_t cost;

92
SAM.cpp
View File

@@ -148,7 +148,7 @@ namespace client
void SAMSocket::SendMessageReply (const char * msg, size_t len, bool close)
{
if (!m_IsSilent || m_SocketType == eSAMSocketTypeAcceptor)
if (!m_IsSilent)
boost::asio::async_write (m_Socket, boost::asio::buffer (msg, len), boost::asio::transfer_all (),
std::bind(&SAMSocket::HandleMessageReplySent, shared_from_this (),
std::placeholders::_1, std::placeholders::_2, close));
@@ -320,13 +320,13 @@ namespace client
i2p::data::IdentityEx dest;
dest.FromBase64 (destination);
context.GetAddressBook ().InsertAddress (dest);
auto leaseSet = i2p::data::netdb.FindLeaseSet (dest.GetIdentHash ());
auto leaseSet = m_Session->localDestination->FindLeaseSet (dest.GetIdentHash ());
if (leaseSet)
Connect (*leaseSet);
Connect (leaseSet);
else
{
m_Session->localDestination->RequestDestination (dest.GetIdentHash (),
std::bind (&SAMSocket::HandleLeaseSetRequestComplete,
std::bind (&SAMSocket::HandleConnectLeaseSetRequestComplete,
shared_from_this (), std::placeholders::_1, dest.GetIdentHash ()));
}
}
@@ -334,7 +334,7 @@ namespace client
SendMessageReply (SAM_STREAM_STATUS_INVALID_ID, strlen(SAM_STREAM_STATUS_INVALID_ID), true);
}
void SAMSocket::Connect (const i2p::data::LeaseSet& remote)
void SAMSocket::Connect (std::shared_ptr<const i2p::data::LeaseSet> remote)
{
m_SocketType = eSAMSocketTypeStream;
m_Session->sockets.push_back (shared_from_this ());
@@ -344,13 +344,13 @@ namespace client
SendMessageReply (SAM_STREAM_STATUS_OK, strlen(SAM_STREAM_STATUS_OK), false);
}
void SAMSocket::HandleLeaseSetRequestComplete (bool success, i2p::data::IdentHash ident)
void SAMSocket::HandleConnectLeaseSetRequestComplete (bool success, i2p::data::IdentHash ident)
{
const i2p::data::LeaseSet * leaseSet = nullptr;
if (success) // timeout expired
std::shared_ptr<const i2p::data::LeaseSet> leaseSet;
if (success)
leaseSet = m_Session->localDestination->FindLeaseSet (ident);
if (leaseSet)
Connect (*leaseSet);
Connect (leaseSet);
else
{
LogPrint ("SAM destination to connect not found");
@@ -409,14 +409,26 @@ namespace client
std::map<std::string, std::string> params;
ExtractParams (buf, len, params);
std::string& name = params[SAM_PARAM_NAME];
i2p::data::IdentHash ident;
i2p::data::IdentityEx identity;
i2p::data::IdentHash ident;
if (name == "ME")
SendNamingLookupReply (nullptr);
SendNamingLookupReply (m_Session->localDestination->GetIdentity ());
else if (context.GetAddressBook ().GetAddress (name, identity))
SendNamingLookupReply (identity);
else if (m_Session && m_Session->localDestination &&
context.GetAddressBook ().GetIdentHash (name, ident))
{
auto leaseSet = m_Session->localDestination->FindLeaseSet (ident);
if (leaseSet)
SendNamingLookupReply (leaseSet->GetIdentity ());
else
m_Session->localDestination->RequestDestination (ident,
std::bind (&SAMSocket::HandleNamingLookupLeaseSetRequestComplete,
shared_from_this (), std::placeholders::_1, ident));
}
else
{
LogPrint ("SAM naming failed. Unknown address ", name);
#ifdef _MSC_VER
size_t len = sprintf_s (m_Buffer, SAM_SOCKET_BUFFER_SIZE, SAM_NAMING_REPLY_INVALID_KEY, name.c_str());
#else
@@ -426,15 +438,30 @@ namespace client
}
}
void SAMSocket::SendNamingLookupReply (const i2p::data::LeaseSet * leaseSet)
void SAMSocket::HandleNamingLookupLeaseSetRequestComplete (bool success, i2p::data::IdentHash ident)
{
const i2p::data::IdentityEx& identity = leaseSet ? leaseSet->GetIdentity () : m_Session->localDestination->GetIdentity ();
std::shared_ptr<const i2p::data::LeaseSet> leaseSet;
if (success)
leaseSet = m_Session->localDestination->FindLeaseSet (ident);
if (leaseSet)
// we found LeaseSet for our address, store it to addressbook
context.GetAddressBook ().InsertAddress (identity);
SendNamingLookupReply (identity);
}
{
context.GetAddressBook ().InsertAddress (leaseSet->GetIdentity ());
SendNamingLookupReply (leaseSet->GetIdentity ());
}
else
{
LogPrint (eLogInfo, "SAM naming lookup failed. LeaseSet for ", ident.ToBase32 (), " not found");
#ifdef _MSC_VER
size_t len = sprintf_s (m_Buffer, SAM_SOCKET_BUFFER_SIZE, SAM_NAMING_REPLY_INVALID_KEY,
context.GetAddressBook ().ToAddress (ident).c_str());
#else
size_t len = snprintf (m_Buffer, SAM_SOCKET_BUFFER_SIZE, SAM_NAMING_REPLY_INVALID_KEY,
context.GetAddressBook ().ToAddress (ident).c_str());
#endif
SendMessageReply (m_Buffer, len, false);
}
}
void SAMSocket::SendNamingLookupReply (const i2p::data::IdentityEx& identity)
{
auto base64 = identity.ToBase64 ();
@@ -539,12 +566,15 @@ namespace client
// send remote peer address
uint8_t ident[1024];
size_t l = stream->GetRemoteIdentity ().ToBuffer (ident, 1024);
size_t l1 = i2p::data::ByteStreamToBase64 (ident, l, m_Buffer, SAM_SOCKET_BUFFER_SIZE);
m_Buffer[l1] = '\n';
SendMessageReply (m_Buffer, l1 + 1, false);
size_t l1 = i2p::data::ByteStreamToBase64 (ident, l, (char *)m_StreamBuffer, SAM_SOCKET_BUFFER_SIZE);
m_StreamBuffer[l1] = '\n';
HandleI2PReceive (boost::system::error_code (), l1 +1); // we send identity like it has been received from stream
}
I2PReceive ();
else
I2PReceive ();
}
else
LogPrint (eLogInfo, "SAM I2P acceptor has been reset");
}
void SAMSocket::HandleI2PDatagramReceive (const i2p::data::IdentityEx& ident, const uint8_t * buf, size_t len)
@@ -650,8 +680,15 @@ namespace client
{
if (!ecode)
{
LogPrint ("New SAM connection from ", socket->GetSocket ().remote_endpoint ());
socket->ReceiveHandshake ();
boost::system::error_code ec;
auto ep = socket->GetSocket ().remote_endpoint (ec);
if (!ec)
{
LogPrint ("New SAM connection from ", ep);
socket->ReceiveHandshake ();
}
else
LogPrint (eLogError, "SAM connection from error ", ec.message ());
}
else
LogPrint ("SAM accept error: ", ecode.message ());
@@ -681,7 +718,7 @@ namespace client
// TODO: extract string values
signatureType = boost::lexical_cast<int> (it->second);
}
localDestination = i2p::client::context.CreateNewLocalDestination (false, signatureType, params);
localDestination = i2p::client::context.CreateNewLocalDestination (true, signatureType, params);
}
if (localDestination)
{
@@ -701,13 +738,14 @@ namespace client
if (it != m_Sessions.end ())
{
auto session = it->second;
session->localDestination->StopAcceptingStreams ();
session->CloseStreams ();
m_Sessions.erase (it);
delete session;
}
}
SAMSession * SAMBridge::FindSession (const std::string& id)
SAMSession * SAMBridge::FindSession (const std::string& id) const
{
std::unique_lock<std::mutex> l(m_SessionsMutex);
auto it = m_Sessions.find (id);
@@ -749,7 +787,7 @@ namespace client
auto leaseSet = i2p::data::netdb.FindLeaseSet (dest.GetIdentHash ());
if (leaseSet)
session->localDestination->GetDatagramDestination ()->
SendDatagramTo ((uint8_t *)eol, payloadLen, *leaseSet);
SendDatagramTo ((uint8_t *)eol, payloadLen, leaseSet);
else
{
LogPrint ("SAM datagram destination not found");

16
SAM.h
View File

@@ -79,6 +79,7 @@ namespace client
boost::asio::ip::tcp::socket& GetSocket () { return m_Socket; };
void ReceiveHandshake ();
void SetSocketType (SAMSocketType socketType) { m_SocketType = socketType; };
SAMSocketType GetSocketType () const { return m_SocketType; };
private:
@@ -104,10 +105,10 @@ namespace client
void ProcessNamingLookup (char * buf, size_t len);
void ExtractParams (char * buf, size_t len, std::map<std::string, std::string>& params);
void Connect (const i2p::data::LeaseSet& remote);
void HandleLeaseSetRequestComplete (bool success, i2p::data::IdentHash ident);
void SendNamingLookupReply (const i2p::data::LeaseSet * leaseSet);
void Connect (std::shared_ptr<const i2p::data::LeaseSet> remote);
void HandleConnectLeaseSetRequestComplete (bool success, i2p::data::IdentHash ident);
void SendNamingLookupReply (const i2p::data::IdentityEx& identity);
void HandleNamingLookupLeaseSetRequestComplete (bool success, i2p::data::IdentHash ident);
void HandleSessionReadinessCheckTimer (const boost::system::error_code& ecode);
void SendSessionCreateReplyOk ();
@@ -150,7 +151,7 @@ namespace client
SAMSession * CreateSession (const std::string& id, const std::string& destination, // empty string means transient
const std::map<std::string, std::string> * params);
void CloseSession (const std::string& id);
SAMSession * FindSession (const std::string& id);
SAMSession * FindSession (const std::string& id) const;
private:
@@ -170,9 +171,14 @@ namespace client
boost::asio::ip::tcp::acceptor m_Acceptor;
boost::asio::ip::udp::endpoint m_DatagramEndpoint, m_SenderEndpoint;
boost::asio::ip::udp::socket m_DatagramSocket;
std::mutex m_SessionsMutex;
mutable std::mutex m_SessionsMutex;
std::map<std::string, SAMSession *> m_Sessions;
uint8_t m_DatagramReceiveBuffer[i2p::datagram::MAX_DATAGRAM_SIZE+1];
public:
// for HTTP
const decltype(m_Sessions)& GetSessions () const { return m_Sessions; };
};
}
}

239
SOCKS.cpp
View File

@@ -1,34 +1,152 @@
#include <cstring>
#include <cassert>
#include <string>
#include <atomic>
#include "SOCKS.h"
#include "Identity.h"
#include "NetDb.h"
#include "Streaming.h"
#include "Destination.h"
#include "ClientContext.h"
#include "I2PEndian.h"
#include <cstring>
#include <cassert>
#include "I2PTunnel.h"
namespace i2p
{
namespace proxy
{
static const size_t socks_buffer_size = 8192;
static const size_t max_socks_hostname_size = 255; // Limit for socks5 and bad idea to traverse
struct SOCKSDnsAddress {
uint8_t size;
char value[max_socks_hostname_size];
void FromString (std::string str) {
size = str.length();
if (str.length() > max_socks_hostname_size) size = max_socks_hostname_size;
memcpy(value,str.c_str(),size);
}
std::string ToString() { return std::string(value, size); }
void push_back (char c) { value[size++] = c; }
};
class SOCKSServer;
class SOCKSHandler: public i2p::client::I2PServiceHandler, public std::enable_shared_from_this<SOCKSHandler> {
private:
enum state {
GET_SOCKSV,
GET_COMMAND,
GET_PORT,
GET_IPV4,
GET4_IDENT,
GET4A_HOST,
GET5_AUTHNUM,
GET5_AUTH,
GET5_REQUESTV,
GET5_GETRSV,
GET5_GETADDRTYPE,
GET5_IPV6,
GET5_HOST_SIZE,
GET5_HOST,
DONE
};
enum authMethods {
AUTH_NONE = 0, //No authentication, skip to next step
AUTH_GSSAPI = 1, //GSSAPI authentication
AUTH_USERPASSWD = 2, //Username and password
AUTH_UNACCEPTABLE = 0xff //No acceptable method found
};
enum addrTypes {
ADDR_IPV4 = 1, //IPv4 address (4 octets)
ADDR_DNS = 3, // DNS name (up to 255 octets)
ADDR_IPV6 = 4 //IPV6 address (16 octets)
};
enum errTypes {
SOCKS5_OK = 0, // No error for SOCKS5
SOCKS5_GEN_FAIL = 1, // General server failure
SOCKS5_RULE_DENIED = 2, // Connection disallowed by ruleset
SOCKS5_NET_UNREACH = 3, // Network unreachable
SOCKS5_HOST_UNREACH = 4, // Host unreachable
SOCKS5_CONN_REFUSED = 5, // Connection refused by the peer
SOCKS5_TTL_EXPIRED = 6, // TTL Expired
SOCKS5_CMD_UNSUP = 7, // Command unsuported
SOCKS5_ADDR_UNSUP = 8, // Address type unsuported
SOCKS4_OK = 90, // No error for SOCKS4
SOCKS4_FAIL = 91, // Failed establishing connecting or not allowed
SOCKS4_IDENTD_MISSING = 92, // Couldn't connect to the identd server
SOCKS4_IDENTD_DIFFER = 93 // The ID reported by the application and by identd differ
};
enum cmdTypes {
CMD_CONNECT = 1, // TCP Connect
CMD_BIND = 2, // TCP Bind
CMD_UDP = 3 // UDP associate
};
enum socksVersions {
SOCKS4 = 4, // SOCKS4
SOCKS5 = 5 // SOCKS5
};
union address {
uint32_t ip;
SOCKSDnsAddress dns;
uint8_t ipv6[16];
};
void EnterState(state nstate, uint8_t parseleft = 1);
bool HandleData(uint8_t *sock_buff, std::size_t len);
bool ValidateSOCKSRequest();
void HandleSockRecv(const boost::system::error_code & ecode, std::size_t bytes_transfered);
void Terminate();
void AsyncSockRead();
boost::asio::const_buffers_1 GenerateSOCKS5SelectAuth(authMethods method);
boost::asio::const_buffers_1 GenerateSOCKS4Response(errTypes error, uint32_t ip, uint16_t port);
boost::asio::const_buffers_1 GenerateSOCKS5Response(errTypes error, addrTypes type, const address &addr, uint16_t port);
bool Socks5ChooseAuth();
void SocksRequestFailed(errTypes error);
void SocksRequestSuccess();
void SentSocksFailed(const boost::system::error_code & ecode);
void SentSocksDone(const boost::system::error_code & ecode);
void SentSocksResponse(const boost::system::error_code & ecode);
void HandleStreamRequestComplete (std::shared_ptr<i2p::stream::Stream> stream);
uint8_t m_sock_buff[socks_buffer_size];
boost::asio::ip::tcp::socket * m_sock;
std::shared_ptr<i2p::stream::Stream> m_stream;
uint8_t *m_remaining_data; //Data left to be sent
uint8_t m_response[7+max_socks_hostname_size];
address m_address; //Address
std::size_t m_remaining_data_len; //Size of the data left to be sent
uint32_t m_4aip; //Used in 4a requests
uint16_t m_port;
uint8_t m_command;
uint8_t m_parseleft; //Octets left to parse
authMethods m_authchosen; //Authentication chosen
addrTypes m_addrtype; //Address type chosen
socksVersions m_socksv; //Socks version
cmdTypes m_cmd; // Command requested
state m_state;
public:
SOCKSHandler(SOCKSServer * parent, boost::asio::ip::tcp::socket * sock) :
I2PServiceHandler(parent), m_sock(sock), m_stream(nullptr),
m_authchosen(AUTH_UNACCEPTABLE), m_addrtype(ADDR_IPV4)
{ m_address.ip = 0; EnterState(GET_SOCKSV); }
~SOCKSHandler() { Terminate(); }
void Handle() { AsyncSockRead(); }
};
void SOCKSHandler::AsyncSockRead()
{
LogPrint(eLogDebug,"--- SOCKS async sock read");
if(m_sock) {
m_sock->async_receive(boost::asio::buffer(m_sock_buff, socks_buffer_size),
std::bind(&SOCKSHandler::HandleSockRecv, this,
std::bind(&SOCKSHandler::HandleSockRecv, shared_from_this(),
std::placeholders::_1, std::placeholders::_2));
} else {
LogPrint(eLogError,"--- SOCKS no socket for read");
}
}
void SOCKSHandler::Done() {
if (m_parent) m_parent->RemoveHandler (shared_from_this ());
}
void SOCKSHandler::Terminate() {
if (dead.exchange(true)) return;
if (Kill()) return;
if (m_sock) {
LogPrint(eLogDebug,"--- SOCKS close sock");
m_sock->close();
@@ -39,7 +157,7 @@ namespace proxy
LogPrint(eLogDebug,"--- SOCKS close stream");
m_stream.reset ();
}
Done();
Done(shared_from_this());
}
boost::asio::const_buffers_1 SOCKSHandler::GenerateSOCKS4Response(SOCKSHandler::errTypes error, uint32_t ip, uint16_t port)
@@ -55,7 +173,7 @@ namespace proxy
boost::asio::const_buffers_1 SOCKSHandler::GenerateSOCKS5Response(SOCKSHandler::errTypes error, SOCKSHandler::addrTypes type,
const SOCKSHandler::address &addr, uint16_t port)
{
size_t size;
size_t size = 6;
assert(error <= SOCKS5_ADDR_UNSUP);
m_response[0] = '\x05'; //Version
m_response[1] = error; //Response code
@@ -87,11 +205,13 @@ namespace proxy
boost::asio::const_buffers_1 response(m_response,2);
if (m_authchosen == AUTH_UNACCEPTABLE) {
LogPrint(eLogWarning,"--- SOCKS5 authentication negotiation failed");
boost::asio::async_write(*m_sock, response, std::bind(&SOCKSHandler::SentSocksFailed, this, std::placeholders::_1));
boost::asio::async_write(*m_sock, response, std::bind(&SOCKSHandler::SentSocksFailed,
shared_from_this(), std::placeholders::_1));
return false;
} else {
LogPrint(eLogDebug,"--- SOCKS5 choosing authentication method: ", m_authchosen);
boost::asio::async_write(*m_sock, response, std::bind(&SOCKSHandler::SentSocksResponse, this, std::placeholders::_1));
boost::asio::async_write(*m_sock, response, std::bind(&SOCKSHandler::SentSocksResponse,
shared_from_this(), std::placeholders::_1));
return true;
}
}
@@ -112,7 +232,8 @@ namespace proxy
response = GenerateSOCKS5Response(error, m_addrtype, m_address, m_port);
break;
}
boost::asio::async_write(*m_sock, response, std::bind(&SOCKSHandler::SentSocksFailed, this, std::placeholders::_1));
boost::asio::async_write(*m_sock, response, std::bind(&SOCKSHandler::SentSocksFailed,
shared_from_this(), std::placeholders::_1));
}
void SOCKSHandler::SocksRequestSuccess()
@@ -126,13 +247,14 @@ namespace proxy
break;
case SOCKS5:
LogPrint(eLogInfo,"--- SOCKS5 connection success");
auto s = i2p::client::context.GetAddressBook().ToAddress(m_parent->GetLocalDestination()->GetIdentHash());
auto s = i2p::client::context.GetAddressBook().ToAddress(GetOwner()->GetLocalDestination()->GetIdentHash());
address ad; ad.dns.FromString(s);
//HACK only 16 bits passed in port as SOCKS5 doesn't allow for more
response = GenerateSOCKS5Response(SOCKS5_OK, ADDR_DNS, ad, m_stream->GetRecvStreamID());
break;
}
boost::asio::async_write(*m_sock, response, std::bind(&SOCKSHandler::SentSocksDone, this, std::placeholders::_1));
boost::asio::async_write(*m_sock, response, std::bind(&SOCKSHandler::SentSocksDone,
shared_from_this(), std::placeholders::_1));
}
void SOCKSHandler::EnterState(SOCKSHandler::state nstate, uint8_t parseleft) {
@@ -149,12 +271,12 @@ namespace proxy
m_state = nstate;
}
void SOCKSHandler::ValidateSOCKSRequest() {
bool SOCKSHandler::ValidateSOCKSRequest() {
if ( m_cmd != CMD_CONNECT ) {
//TODO: we need to support binds and other shit!
LogPrint(eLogError,"--- SOCKS unsupported command: ", m_cmd);
SocksRequestFailed(SOCKS5_CMD_UNSUP);
return;
return false;
}
//TODO: we may want to support other address types!
if ( m_addrtype != ADDR_DNS ) {
@@ -167,14 +289,15 @@ namespace proxy
break;
}
SocksRequestFailed(SOCKS5_ADDR_UNSUP);
return;
return false;
}
//TODO: we may want to support other domains
if(m_addrtype == ADDR_DNS && m_address.dns.ToString().find(".i2p") == std::string::npos) {
LogPrint(eLogError,"--- SOCKS invalid hostname: ", m_address.dns.ToString());
SocksRequestFailed(SOCKS5_ADDR_UNSUP);
return;
return false;
}
return true;
}
bool SOCKSHandler::HandleData(uint8_t *sock_buff, std::size_t len)
@@ -315,10 +438,10 @@ namespace proxy
}
sock_buff++;
len--;
if (len && m_state == DONE) {
LogPrint(eLogError,"--- SOCKS rejected because we can't handle extra data");
SocksRequestFailed(SOCKS5_GEN_FAIL);
return false;
if (m_state == DONE) {
m_remaining_data_len = len;
m_remaining_data = sock_buff;
return ValidateSOCKSRequest();
}
}
return true;
@@ -336,9 +459,8 @@ namespace proxy
if (HandleData(m_sock_buff, len)) {
if (m_state == DONE) {
LogPrint(eLogInfo,"--- SOCKS requested ", m_address.dns.ToString(), ":" , m_port);
m_parent->GetLocalDestination ()->CreateStream (
std::bind (&SOCKSHandler::HandleStreamRequestComplete,
this, std::placeholders::_1), m_address.dns.ToString(), m_port);
GetOwner()->CreateStream ( std::bind (&SOCKSHandler::HandleStreamRequestComplete,
shared_from_this(), std::placeholders::_1), m_address.dns.ToString(), m_port);
} else {
AsyncSockRead();
}
@@ -359,12 +481,12 @@ namespace proxy
void SOCKSHandler::SentSocksDone(const boost::system::error_code & ecode)
{
if (!ecode) {
if (dead.exchange(true)) return;
if (Kill()) return;
LogPrint (eLogInfo,"--- SOCKS New I2PTunnel connection");
auto connection = std::make_shared<i2p::client::I2PTunnelConnection>((i2p::client::I2PTunnel *)m_parent, m_sock, m_stream);
m_parent->AddConnection (connection);
connection->I2PConnect ();
Done();
auto connection = std::make_shared<i2p::client::I2PTunnelConnection>(GetOwner(), m_sock, m_stream);
GetOwner()->AddHandler (connection);
connection->I2PConnect (m_remaining_data,m_remaining_data_len);
Done(shared_from_this());
}
else
{
@@ -392,57 +514,14 @@ namespace proxy
}
}
void SOCKSServer::Start ()
SOCKSServer::SOCKSServer(int port) :
TCPIPAcceptor (port, i2p::client::context.GetSharedLocalDestination ())
{
m_Acceptor.listen ();
Accept ();
}
void SOCKSServer::Stop ()
std::shared_ptr<i2p::client::I2PServiceHandler> SOCKSServer::CreateHandler(boost::asio::ip::tcp::socket * socket)
{
m_Acceptor.close();
m_Timer.cancel ();
ClearConnections ();
ClearHandlers();
}
void SOCKSServer::Accept ()
{
auto newSocket = new boost::asio::ip::tcp::socket (GetService ());
m_Acceptor.async_accept (*newSocket, std::bind (&SOCKSServer::HandleAccept, this,
std::placeholders::_1, newSocket));
}
void SOCKSServer::AddHandler (std::shared_ptr<SOCKSHandler> handler) {
std::unique_lock<std::mutex> l(m_HandlersMutex);
m_Handlers.insert (handler);
}
void SOCKSServer::RemoveHandler (std::shared_ptr<SOCKSHandler> handler)
{
std::unique_lock<std::mutex> l(m_HandlersMutex);
m_Handlers.erase (handler);
}
void SOCKSServer::ClearHandlers ()
{
std::unique_lock<std::mutex> l(m_HandlersMutex);
m_Handlers.clear ();
}
void SOCKSServer::HandleAccept (const boost::system::error_code& ecode, boost::asio::ip::tcp::socket * socket)
{
if (!ecode)
{
LogPrint(eLogDebug,"--- SOCKS accepted");
AddHandler(std::make_shared<SOCKSHandler> (this, socket));
Accept();
}
else
{
LogPrint (eLogError,"--- SOCKS Closing socket on accept because: ", ecode.message ());
delete socket;
}
return std::make_shared<SOCKSHandler> (this, socket);
}
}

154
SOCKS.h
View File

@@ -2,163 +2,25 @@
#define SOCKS_H__
#include <memory>
#include <string>
#include <set>
#include <boost/asio.hpp>
#include <mutex>
#include <atomic>
#include "Identity.h"
#include "Streaming.h"
#include "I2PTunnel.h"
#include "I2PService.h"
namespace i2p
{
namespace proxy
{
const size_t socks_buffer_size = 8192;
const size_t max_socks_hostname_size = 255; // Limit for socks5 and bad idea to traverse
struct SOCKSDnsAddress {
uint8_t size;
char value[max_socks_hostname_size];
void FromString (std::string str) {
size = str.length();
if (str.length() > max_socks_hostname_size) size = max_socks_hostname_size;
memcpy(value,str.c_str(),size);
}
std::string ToString() { return std::string(value, size); }
void push_back (char c) { value[size++] = c; }
};
class SOCKSServer;
class SOCKSHandler: public std::enable_shared_from_this<SOCKSHandler> {
private:
enum state {
GET_SOCKSV,
GET_COMMAND,
GET_PORT,
GET_IPV4,
GET4_IDENT,
GET4A_HOST,
GET5_AUTHNUM,
GET5_AUTH,
GET5_REQUESTV,
GET5_GETRSV,
GET5_GETADDRTYPE,
GET5_IPV6,
GET5_HOST_SIZE,
GET5_HOST,
DONE
};
enum authMethods {
AUTH_NONE = 0, //No authentication, skip to next step
AUTH_GSSAPI = 1, //GSSAPI authentication
AUTH_USERPASSWD = 2, //Username and password
AUTH_UNACCEPTABLE = 0xff //No acceptable method found
};
enum addrTypes {
ADDR_IPV4 = 1, //IPv4 address (4 octets)
ADDR_DNS = 3, // DNS name (up to 255 octets)
ADDR_IPV6 = 4 //IPV6 address (16 octets)
};
enum errTypes {
SOCKS5_OK = 0, // No error for SOCKS5
SOCKS5_GEN_FAIL = 1, // General server failure
SOCKS5_RULE_DENIED = 2, // Connection disallowed by ruleset
SOCKS5_NET_UNREACH = 3, // Network unreachable
SOCKS5_HOST_UNREACH = 4, // Host unreachable
SOCKS5_CONN_REFUSED = 5, // Connection refused by the peer
SOCKS5_TTL_EXPIRED = 6, // TTL Expired
SOCKS5_CMD_UNSUP = 7, // Command unsuported
SOCKS5_ADDR_UNSUP = 8, // Address type unsuported
SOCKS4_OK = 90, // No error for SOCKS4
SOCKS4_FAIL = 91, // Failed establishing connecting or not allowed
SOCKS4_IDENTD_MISSING = 92, // Couldn't connect to the identd server
SOCKS4_IDENTD_DIFFER = 93 // The ID reported by the application and by identd differ
};
enum cmdTypes {
CMD_CONNECT = 1, // TCP Connect
CMD_BIND = 2, // TCP Bind
CMD_UDP = 3 // UDP associate
};
enum socksVersions {
SOCKS4 = 4, // SOCKS4
SOCKS5 = 5 // SOCKS5
};
union address {
uint32_t ip;
SOCKSDnsAddress dns;
uint8_t ipv6[16];
};
void EnterState(state nstate, uint8_t parseleft = 1);
bool HandleData(uint8_t *sock_buff, std::size_t len);
void ValidateSOCKSRequest();
void HandleSockRecv(const boost::system::error_code & ecode, std::size_t bytes_transfered);
void Done();
void Terminate();
void AsyncSockRead();
boost::asio::const_buffers_1 GenerateSOCKS5SelectAuth(authMethods method);
boost::asio::const_buffers_1 GenerateSOCKS4Response(errTypes error, uint32_t ip, uint16_t port);
boost::asio::const_buffers_1 GenerateSOCKS5Response(errTypes error, addrTypes type, const address &addr, uint16_t port);
bool Socks5ChooseAuth();
void SocksRequestFailed(errTypes error);
void SocksRequestSuccess();
void SentSocksFailed(const boost::system::error_code & ecode);
void SentSocksDone(const boost::system::error_code & ecode);
void SentSocksResponse(const boost::system::error_code & ecode);
void HandleStreamRequestComplete (std::shared_ptr<i2p::stream::Stream> stream);
uint8_t m_sock_buff[socks_buffer_size];
SOCKSServer * m_parent;
boost::asio::ip::tcp::socket * m_sock;
std::shared_ptr<i2p::stream::Stream> m_stream;
uint8_t m_response[7+max_socks_hostname_size];
address m_address; //Address
uint32_t m_4aip; //Used in 4a requests
uint16_t m_port;
uint8_t m_command;
uint8_t m_parseleft; //Octets left to parse
authMethods m_authchosen; //Authentication chosen
addrTypes m_addrtype; //Address type chosen
socksVersions m_socksv; //Socks version
cmdTypes m_cmd; // Command requested
state m_state;
std::atomic<bool> dead; //To avoid cleaning up multiple times
public:
SOCKSHandler(SOCKSServer * parent, boost::asio::ip::tcp::socket * sock) :
m_parent(parent), m_sock(sock), m_stream(nullptr),
m_authchosen(AUTH_UNACCEPTABLE), m_addrtype(ADDR_IPV4), dead(false)
{ m_address.ip = 0; AsyncSockRead(); EnterState(GET_SOCKSV); }
~SOCKSHandler() { Terminate(); }
};
class SOCKSServer: public i2p::client::I2PTunnel
class SOCKSServer: public i2p::client::TCPIPAcceptor
{
private:
std::set<std::shared_ptr<SOCKSHandler> > m_Handlers;
boost::asio::ip::tcp::acceptor m_Acceptor;
boost::asio::deadline_timer m_Timer;
std::mutex m_HandlersMutex;
private:
void Accept();
void HandleAccept(const boost::system::error_code& ecode, boost::asio::ip::tcp::socket * socket);
protected:
// Implements TCPIPAcceptor
std::shared_ptr<i2p::client::I2PServiceHandler> CreateHandler(boost::asio::ip::tcp::socket * socket);
const char* GetName() { return "SOCKS"; }
public:
SOCKSServer(int port) : I2PTunnel(nullptr),
m_Acceptor (GetService (), boost::asio::ip::tcp::endpoint (boost::asio::ip::tcp::v4(), port)),
m_Timer (GetService ()) {};
~SOCKSServer() { Stop(); }
void Start ();
void Stop ();
void AddHandler (std::shared_ptr<SOCKSHandler> handler);
void RemoveHandler (std::shared_ptr<SOCKSHandler> handler);
void ClearHandlers ();
SOCKSServer(int port);
~SOCKSServer() {}
};
typedef SOCKSServer SOCKSProxy;

137
SSU.cpp
View File

@@ -9,10 +9,11 @@ namespace i2p
{
namespace transport
{
SSUServer::SSUServer (int port): m_Thread (nullptr), m_ThreadV6 (nullptr), m_Work (m_Service),
m_WorkV6 (m_ServiceV6),m_Endpoint (boost::asio::ip::udp::v4 (), port),
m_EndpointV6 (boost::asio::ip::udp::v6 (), port), m_Socket (m_Service, m_Endpoint),
m_SocketV6 (m_ServiceV6), m_IntroducersUpdateTimer (m_Service)
SSUServer::SSUServer (int port): m_Thread (nullptr), m_ThreadV6 (nullptr), m_ReceiversThread (nullptr),
m_Work (m_Service), m_WorkV6 (m_ServiceV6), m_ReceiversWork (m_ReceiversService),
m_Endpoint (boost::asio::ip::udp::v4 (), port), m_EndpointV6 (boost::asio::ip::udp::v6 (), port),
m_Socket (m_ReceiversService, m_Endpoint), m_SocketV6 (m_ReceiversService),
m_IntroducersUpdateTimer (m_Service)
{
m_Socket.set_option (boost::asio::socket_base::receive_buffer_size (65535));
m_Socket.set_option (boost::asio::socket_base::send_buffer_size (65535));
@@ -33,12 +34,13 @@ namespace transport
void SSUServer::Start ()
{
m_IsRunning = true;
m_ReceiversThread = new std::thread (std::bind (&SSUServer::RunReceivers, this));
m_Thread = new std::thread (std::bind (&SSUServer::Run, this));
m_Service.post (std::bind (&SSUServer::Receive, this));
m_ReceiversService.post (std::bind (&SSUServer::Receive, this));
if (context.SupportsV6 ())
{
m_ThreadV6 = new std::thread (std::bind (&SSUServer::RunV6, this));
m_ServiceV6.post (std::bind (&SSUServer::ReceiveV6, this));
m_ReceiversService.post (std::bind (&SSUServer::ReceiveV6, this));
}
if (i2p::context.IsUnreachable ())
ScheduleIntroducersUpdateTimer ();
@@ -52,6 +54,13 @@ namespace transport
m_Socket.close ();
m_ServiceV6.stop ();
m_SocketV6.close ();
m_ReceiversService.stop ();
if (m_ReceiversThread)
{
m_ReceiversThread->join ();
delete m_ReceiversThread;
m_ReceiversThread = nullptr;
}
if (m_Thread)
{
m_Thread->join ();
@@ -95,6 +104,21 @@ namespace transport
}
}
}
void SSUServer::RunReceivers ()
{
while (m_IsRunning)
{
try
{
m_ReceiversService.run ();
}
catch (std::exception& ex)
{
LogPrint (eLogError, "SSU receivers: ", ex.what ());
}
}
}
void SSUServer::AddRelay (uint32_t tag, const boost::asio::ip::udp::endpoint& relay)
{
@@ -119,52 +143,100 @@ namespace transport
void SSUServer::Receive ()
{
m_Socket.async_receive_from (boost::asio::buffer (m_ReceiveBuffer, SSU_MTU_V4), m_SenderEndpoint,
boost::bind (&SSUServer::HandleReceivedFrom, this, boost::asio::placeholders::error, boost::asio::placeholders::bytes_transferred));
SSUPacket * packet = new SSUPacket ();
m_Socket.async_receive_from (boost::asio::buffer (packet->buf, SSU_MTU_V4), packet->from,
std::bind (&SSUServer::HandleReceivedFrom, this, std::placeholders::_1, std::placeholders::_2, packet));
}
void SSUServer::ReceiveV6 ()
{
m_SocketV6.async_receive_from (boost::asio::buffer (m_ReceiveBufferV6, SSU_MTU_V6), m_SenderEndpointV6,
boost::bind (&SSUServer::HandleReceivedFromV6, this, boost::asio::placeholders::error, boost::asio::placeholders::bytes_transferred));
SSUPacket * packet = new SSUPacket ();
m_SocketV6.async_receive_from (boost::asio::buffer (packet->buf, SSU_MTU_V6), packet->from,
std::bind (&SSUServer::HandleReceivedFromV6, this, std::placeholders::_1, std::placeholders::_2, packet));
}
void SSUServer::HandleReceivedFrom (const boost::system::error_code& ecode, std::size_t bytes_transferred)
void SSUServer::HandleReceivedFrom (const boost::system::error_code& ecode, std::size_t bytes_transferred, SSUPacket * packet)
{
if (!ecode)
{
HandleReceivedBuffer (m_SenderEndpoint, m_ReceiveBuffer, bytes_transferred);
packet->len = bytes_transferred;
std::vector<SSUPacket *> packets;
packets.push_back (packet);
boost::system::error_code ec;
size_t moreBytes = m_Socket.available(ec);
while (moreBytes && packets.size () < 25)
{
packet = new SSUPacket ();
packet->len = m_Socket.receive_from (boost::asio::buffer (packet->buf, SSU_MTU_V4), packet->from);
packets.push_back (packet);
moreBytes = m_Socket.available();
}
m_Service.post (std::bind (&SSUServer::HandleReceivedPackets, this, packets));
Receive ();
}
else
{
LogPrint ("SSU receive error: ", ecode.message ());
delete packet;
}
}
void SSUServer::HandleReceivedFromV6 (const boost::system::error_code& ecode, std::size_t bytes_transferred)
void SSUServer::HandleReceivedFromV6 (const boost::system::error_code& ecode, std::size_t bytes_transferred, SSUPacket * packet)
{
if (!ecode)
{
HandleReceivedBuffer (m_SenderEndpointV6, m_ReceiveBufferV6, bytes_transferred);
packet->len = bytes_transferred;
std::vector<SSUPacket *> packets;
packets.push_back (packet);
size_t moreBytes = m_SocketV6.available ();
while (moreBytes && packets.size () < 25)
{
packet = new SSUPacket ();
packet->len = m_SocketV6.receive_from (boost::asio::buffer (packet->buf, SSU_MTU_V6), packet->from);
packets.push_back (packet);
moreBytes = m_SocketV6.available();
}
m_ServiceV6.post (std::bind (&SSUServer::HandleReceivedPackets, this, packets));
ReceiveV6 ();
}
else
{
LogPrint ("SSU V6 receive error: ", ecode.message ());
delete packet;
}
}
void SSUServer::HandleReceivedBuffer (boost::asio::ip::udp::endpoint& from, uint8_t * buf, std::size_t bytes_transferred)
void SSUServer::HandleReceivedPackets (std::vector<SSUPacket *> packets)
{
std::shared_ptr<SSUSession> session;
auto it = m_Sessions.find (from);
if (it != m_Sessions.end ())
session = it->second;
if (!session)
std::shared_ptr<SSUSession> session;
for (auto it1: packets)
{
session = std::make_shared<SSUSession> (*this, from);
session->WaitForConnect ();
m_Sessions[from] = session;
LogPrint ("New SSU session from ", from.address ().to_string (), ":", from.port (), " created");
auto packet = it1;
if (!session || session->GetRemoteEndpoint () != packet->from) // we received packet for other session than previous
{
if (session) session->FlushData ();
auto it = m_Sessions.find (packet->from);
if (it != m_Sessions.end ())
session = it->second;
if (!session)
{
session = std::make_shared<SSUSession> (*this, packet->from);
session->WaitForConnect ();
{
std::unique_lock<std::mutex> l(m_SessionsMutex);
m_Sessions[packet->from] = session;
}
LogPrint ("New SSU session from ", packet->from.address ().to_string (), ":", packet->from.port (), " created");
}
}
session->ProcessNextMessage (packet->buf, packet->len, packet->from);
delete packet;
}
session->ProcessNextMessage (buf, bytes_transferred, from);
if (session) session->FlushData ();
}
std::shared_ptr<SSUSession> SSUServer::FindSession (std::shared_ptr<const i2p::data::RouterInfo> router) const
@@ -206,8 +278,10 @@ namespace transport
{
// otherwise create new session
session = std::make_shared<SSUSession> (*this, remoteEndpoint, router, peerTest);
m_Sessions[remoteEndpoint] = session;
{
std::unique_lock<std::mutex> l(m_SessionsMutex);
m_Sessions[remoteEndpoint] = session;
}
if (!router->UsesIntroducer ())
{
// connect directly
@@ -243,6 +317,7 @@ namespace transport
introducer = &(address->introducers[0]); // TODO:
boost::asio::ip::udp::endpoint introducerEndpoint (introducer->iHost, introducer->iPort);
introducerSession = std::make_shared<SSUSession> (*this, introducerEndpoint, router);
std::unique_lock<std::mutex> l(m_SessionsMutex);
m_Sessions[introducerEndpoint] = introducerSession;
}
// introduce
@@ -250,12 +325,16 @@ namespace transport
"] through introducer ", introducer->iHost, ":", introducer->iPort);
session->WaitForIntroduction ();
if (i2p::context.GetRouterInfo ().UsesIntroducer ()) // if we are unreachable
Send (m_ReceiveBuffer, 0, remoteEndpoint); // send HolePunch
{
uint8_t buf[1];
Send (buf, 0, remoteEndpoint); // send HolePunch
}
introducerSession->Introduce (introducer->iTag, introducer->iKey);
}
else
{
LogPrint (eLogWarning, "Can't connect to unreachable router. No introducers presented");
std::unique_lock<std::mutex> l(m_SessionsMutex);
m_Sessions.erase (remoteEndpoint);
session.reset ();
}
@@ -273,12 +352,14 @@ namespace transport
if (session)
{
session->Close ();
std::unique_lock<std::mutex> l(m_SessionsMutex);
m_Sessions.erase (session->GetRemoteEndpoint ());
}
}
void SSUServer::DeleteAllSessions ()
{
std::unique_lock<std::mutex> l(m_SessionsMutex);
for (auto it: m_Sessions)
it.second->Close ();
m_Sessions.clear ();

28
SSU.h
View File

@@ -7,6 +7,7 @@
#include <list>
#include <set>
#include <thread>
#include <mutex>
#include <boost/asio.hpp>
#include "aes.h"
#include "I2PEndian.h"
@@ -22,6 +23,13 @@ namespace transport
const int SSU_KEEP_ALIVE_INTERVAL = 30; // 30 seconds
const int SSU_TO_INTRODUCER_SESSION_DURATION = 3600; // 1 hour
const size_t SSU_MAX_NUM_INTRODUCERS = 3;
struct SSUPacket
{
i2p::crypto::AESAlignedBuffer<1500> buf;
boost::asio::ip::udp::endpoint from;
size_t len;
};
class SSUServer
{
@@ -38,7 +46,8 @@ namespace transport
void DeleteSession (std::shared_ptr<SSUSession> session);
void DeleteAllSessions ();
boost::asio::io_service& GetService () { return m_Socket.get_io_service(); };
boost::asio::io_service& GetService () { return m_Service; };
boost::asio::io_service& GetServiceV6 () { return m_ServiceV6; };
const boost::asio::ip::udp::endpoint& GetEndpoint () const { return m_Endpoint; };
void Send (const uint8_t * buf, size_t len, const boost::asio::ip::udp::endpoint& to);
void AddRelay (uint32_t tag, const boost::asio::ip::udp::endpoint& relay);
@@ -48,11 +57,12 @@ namespace transport
void Run ();
void RunV6 ();
void RunReceivers ();
void Receive ();
void ReceiveV6 ();
void HandleReceivedFrom (const boost::system::error_code& ecode, std::size_t bytes_transferred);
void HandleReceivedFromV6 (const boost::system::error_code& ecode, std::size_t bytes_transferred);
void HandleReceivedBuffer (boost::asio::ip::udp::endpoint& from, uint8_t * buf, std::size_t bytes_transferred);
void HandleReceivedFrom (const boost::system::error_code& ecode, std::size_t bytes_transferred, SSUPacket * packet);
void HandleReceivedFromV6 (const boost::system::error_code& ecode, std::size_t bytes_transferred, SSUPacket * packet);
void HandleReceivedPackets (std::vector<SSUPacket *> packets);
template<typename Filter>
std::shared_ptr<SSUSession> GetRandomSession (Filter filter);
@@ -64,16 +74,14 @@ namespace transport
private:
bool m_IsRunning;
std::thread * m_Thread, * m_ThreadV6;
boost::asio::io_service m_Service, m_ServiceV6;
boost::asio::io_service::work m_Work, m_WorkV6;
std::thread * m_Thread, * m_ThreadV6, * m_ReceiversThread;
boost::asio::io_service m_Service, m_ServiceV6, m_ReceiversService;
boost::asio::io_service::work m_Work, m_WorkV6, m_ReceiversWork;
boost::asio::ip::udp::endpoint m_Endpoint, m_EndpointV6;
boost::asio::ip::udp::socket m_Socket, m_SocketV6;
boost::asio::ip::udp::endpoint m_SenderEndpoint, m_SenderEndpointV6;
boost::asio::deadline_timer m_IntroducersUpdateTimer;
std::list<boost::asio::ip::udp::endpoint> m_Introducers; // introducers we are connected to
i2p::crypto::AESAlignedBuffer<2*SSU_MTU_V4> m_ReceiveBuffer;
i2p::crypto::AESAlignedBuffer<2*SSU_MTU_V6> m_ReceiveBufferV6;
std::mutex m_SessionsMutex;
std::map<boost::asio::ip::udp::endpoint, std::shared_ptr<SSUSession> > m_Sessions;
std::map<uint32_t, boost::asio::ip::udp::endpoint> m_Relays; // we are introducer

View File

@@ -11,7 +11,8 @@ namespace i2p
namespace transport
{
SSUData::SSUData (SSUSession& session):
m_Session (session), m_ResendTimer (session.m_Server.GetService ())
m_Session (session), m_ResendTimer (session.GetService ()), m_DecayTimer (session.GetService ()),
m_IncompleteMessagesCleanupTimer (session.GetService ())
{
m_MaxPacketSize = session.IsV6 () ? SSU_V6_MAX_PACKET_SIZE : SSU_V4_MAX_PACKET_SIZE;
m_PacketSize = m_MaxPacketSize;
@@ -22,16 +23,20 @@ namespace transport
SSUData::~SSUData ()
{
for (auto it: m_IncomleteMessages)
if (it.second)
{
DeleteI2NPMessage (it.second->msg);
delete it.second;
}
for (auto it: m_SentMessages)
delete it.second;
}
void SSUData::Start ()
{
ScheduleIncompleteMessagesCleanup ();
}
void SSUData::Stop ()
{
m_ResendTimer.cancel ();
m_DecayTimer.cancel ();
m_IncompleteMessagesCleanupTimer.cancel ();
}
void SSUData::AdjustPacketSize (const i2p::data::RouterInfo& remoteRouter)
{
auto ssuAddress = remoteRouter.GetSSUAddress ();
@@ -69,7 +74,6 @@ namespace transport
auto it = m_SentMessages.find (msgID);
if (it != m_SentMessages.end ())
{
delete it->second;
m_SentMessages.erase (it);
if (m_SentMessages.empty ())
m_ResendTimer.cancel ();
@@ -115,10 +119,7 @@ namespace transport
if (bitfield & mask)
{
if (fragment < numSentFragments)
{
delete it->second->fragments[fragment];
it->second->fragments[fragment] = nullptr;
}
it->second->fragments[fragment].reset (nullptr);
}
fragment++;
mask <<= 1;
@@ -146,8 +147,7 @@ namespace transport
uint32_t fragmentInfo = bufbe32toh (frag); // fragment info
uint16_t fragmentSize = fragmentInfo & 0x1FFF; // bits 0 - 13
bool isLast = fragmentInfo & 0x010000; // bit 16
uint8_t fragmentNum = fragmentInfo >> 17; // bits 23 - 17
LogPrint (eLogDebug, "SSU data fragment ", (int)fragmentNum, " of message ", msgID, " size=", (int)fragmentSize, isLast ? " last" : " non-last");
uint8_t fragmentNum = fragmentInfo >> 17; // bits 23 - 17
if (fragmentSize >= SSU_V4_MAX_PACKET_SIZE)
{
LogPrint (eLogError, "Fragment size ", fragmentSize, "exceeds max SSU packet size");
@@ -156,22 +156,19 @@ namespace transport
// find message with msgID
I2NPMessage * msg = nullptr;
IncompleteMessage * incompleteMessage = nullptr;
auto it = m_IncomleteMessages.find (msgID);
if (it != m_IncomleteMessages.end ())
{
auto it = m_IncompleteMessages.find (msgID);
if (it != m_IncompleteMessages.end ())
// message exists
incompleteMessage = it->second;
msg = incompleteMessage->msg;
}
msg = it->second->msg;
else
{
// create new message
msg = NewI2NPMessage ();
msg->len -= I2NP_SHORT_HEADER_SIZE;
incompleteMessage = new IncompleteMessage (msg);
m_IncomleteMessages[msgID] = incompleteMessage;
}
it = m_IncompleteMessages.insert (std::make_pair (msgID,
std::unique_ptr<IncompleteMessage>(new IncompleteMessage (msg)))).first;
}
std::unique_ptr<IncompleteMessage>& incompleteMessage = it->second;
// handle current fragment
if (fragmentNum == incompleteMessage->nextFragmentNum)
@@ -185,7 +182,7 @@ namespace transport
// try saved fragments
for (auto it1 = incompleteMessage->savedFragments.begin (); it1 != incompleteMessage->savedFragments.end ();)
{
auto savedFragment = *it1;
auto& savedFragment = *it1;
if (savedFragment->fragmentNum == incompleteMessage->nextFragmentNum)
{
memcpy (msg->buf + msg->len, savedFragment->buf, savedFragment->len);
@@ -193,7 +190,6 @@ namespace transport
isLast = savedFragment->isLast;
incompleteMessage->nextFragmentNum++;
incompleteMessage->savedFragments.erase (it1++);
delete savedFragment;
}
else
break;
@@ -212,11 +208,10 @@ namespace transport
// missing fragment
LogPrint (eLogWarning, "Missing fragments from ", (int)incompleteMessage->nextFragmentNum, " to ", fragmentNum - 1, " of message ", msgID);
auto savedFragment = new Fragment (fragmentNum, buf, fragmentSize, isLast);
if (!incompleteMessage->savedFragments.insert (savedFragment).second)
{
if (incompleteMessage->savedFragments.insert (std::unique_ptr<Fragment>(savedFragment)).second)
incompleteMessage->lastFragmentInsertTime = i2p::util::GetSecondsSinceEpoch ();
else
LogPrint (eLogWarning, "Fragment ", (int)fragmentNum, " of message ", msgID, " already saved");
delete savedFragment;
}
}
isLast = false;
}
@@ -224,8 +219,8 @@ namespace transport
if (isLast)
{
// delete incomplete message
delete incompleteMessage;
m_IncomleteMessages.erase (msgID);
incompleteMessage->msg = nullptr;
m_IncompleteMessages.erase (msgID);
// process message
SendMsgAck (msgID);
msg->FromSSU (msgID);
@@ -233,9 +228,12 @@ namespace transport
{
if (!m_ReceivedMessages.count (msgID))
{
if (m_ReceivedMessages.size () > 100) m_ReceivedMessages.clear ();
if (m_ReceivedMessages.size () > MAX_NUM_RECEIVED_MESSAGES)
m_ReceivedMessages.clear ();
else
ScheduleDecay ();
m_ReceivedMessages.insert (msgID);
i2p::HandleI2NPMessage (msg);
m_Handler.PutNextMessage (msg);
}
else
{
@@ -262,12 +260,17 @@ namespace transport
}
}
void SSUData::FlushReceivedMessage ()
{
m_Handler.Flush ();
}
void SSUData::ProcessMessage (uint8_t * buf, size_t len)
{
//uint8_t * start = buf;
uint8_t flag = *buf;
buf++;
LogPrint (eLogDebug, "Process SSU data flags=", (int)flag);
LogPrint (eLogDebug, "Process SSU data flags=", (int)flag, " len=", len);
// process acks if presented
if (flag & (DATA_FLAG_ACK_BITFIELDS_INCLUDED | DATA_FLAG_EXPLICIT_ACKS_INCLUDED))
ProcessAcks (buf, flag);
@@ -294,12 +297,15 @@ namespace transport
}
if (m_SentMessages.empty ()) // schedule resend at first message only
ScheduleResend ();
SentMessage * sentMessage = new SentMessage;
m_SentMessages[msgID] = sentMessage;
sentMessage->nextResendTime = i2p::util::GetSecondsSinceEpoch () + RESEND_INTERVAL;
sentMessage->numResends = 0;
auto ret = m_SentMessages.insert (std::make_pair (msgID, std::unique_ptr<SentMessage>(new SentMessage)));
std::unique_ptr<SentMessage>& sentMessage = ret.first->second;
if (ret.second)
{
sentMessage->nextResendTime = i2p::util::GetSecondsSinceEpoch () + RESEND_INTERVAL;
sentMessage->numResends = 0;
}
auto& fragments = sentMessage->fragments;
msgID = htobe32 (msgID);
size_t payloadSize = m_PacketSize - sizeof (SSUHeader) - 9; // 9 = flag + #frg(1) + messageID(4) + frag info (3)
size_t len = msg->GetLength ();
uint8_t * msgBuf = msg->GetSSUHeader ();
@@ -310,13 +316,12 @@ namespace transport
Fragment * fragment = new Fragment;
fragment->fragmentNum = fragmentNum;
uint8_t * buf = fragment->buf;
fragments.push_back (fragment);
uint8_t * payload = buf + sizeof (SSUHeader);
*payload = DATA_FLAG_WANT_REPLY; // for compatibility
payload++;
*payload = 1; // always 1 message fragment per message
payload++;
*(uint32_t *)payload = msgID;
htobe32buf (payload, msgID);
payload += 4;
bool isLast = (len <= payloadSize);
size_t size = isLast ? len : payloadSize;
@@ -334,11 +339,18 @@ namespace transport
if (size & 0x0F) // make sure 16 bytes boundary
size = ((size >> 4) + 1) << 4; // (/16 + 1)*16
fragment->len = size;
fragments.push_back (std::unique_ptr<Fragment> (fragment));
// encrypt message with session key
m_Session.FillHeaderAndEncrypt (PAYLOAD_TYPE_DATA, buf, size);
m_Session.Send (buf, size);
try
{
m_Session.Send (buf, size);
}
catch (boost::system::system_error& ec)
{
LogPrint (eLogError, "Can't send SSU fragment ", ec.what ());
}
if (!isLast)
{
len -= payloadSize;
@@ -405,26 +417,90 @@ namespace transport
m_ResendTimer.async_wait ([s](const boost::system::error_code& ecode)
{ s->m_Data.HandleResendTimer (ecode); });
}
void SSUData::HandleResendTimer (const boost::system::error_code& ecode)
{
if (ecode != boost::asio::error::operation_aborted)
{
uint32_t ts = i2p::util::GetSecondsSinceEpoch ();
for (auto it : m_SentMessages)
for (auto it = m_SentMessages.begin (); it != m_SentMessages.end ();)
{
if (ts >= it.second->nextResendTime && it.second->numResends < MAX_NUM_RESENDS)
if (ts >= it->second->nextResendTime)
{
for (auto f: it.second->fragments)
if (f) m_Session.Send (f->buf, f->len); // resend
if (it->second->numResends < MAX_NUM_RESENDS)
{
for (auto& f: it->second->fragments)
if (f)
{
try
{
m_Session.Send (f->buf, f->len); // resend
}
catch (boost::system::system_error& ec)
{
LogPrint (eLogError, "Can't resend SSU fragment ", ec.what ());
}
}
it.second->numResends++;
it.second->nextResendTime += it.second->numResends*RESEND_INTERVAL;
it->second->numResends++;
it->second->nextResendTime += it->second->numResends*RESEND_INTERVAL;
it++;
}
else
{
LogPrint (eLogError, "SSU message has not been ACKed after ", MAX_NUM_RESENDS, " attempts. Deleted");
it = m_SentMessages.erase (it);
}
}
else
it++;
}
ScheduleResend ();
}
}
void SSUData::ScheduleDecay ()
{
m_DecayTimer.cancel ();
m_DecayTimer.expires_from_now (boost::posix_time::seconds(DECAY_INTERVAL));
auto s = m_Session.shared_from_this();
m_ResendTimer.async_wait ([s](const boost::system::error_code& ecode)
{ s->m_Data.HandleDecayTimer (ecode); });
}
void SSUData::HandleDecayTimer (const boost::system::error_code& ecode)
{
if (ecode != boost::asio::error::operation_aborted)
m_ReceivedMessages.clear ();
}
void SSUData::ScheduleIncompleteMessagesCleanup ()
{
m_IncompleteMessagesCleanupTimer.cancel ();
m_IncompleteMessagesCleanupTimer.expires_from_now (boost::posix_time::seconds(INCOMPLETE_MESSAGES_CLEANUP_TIMEOUT));
auto s = m_Session.shared_from_this();
m_IncompleteMessagesCleanupTimer.async_wait ([s](const boost::system::error_code& ecode)
{ s->m_Data.HandleIncompleteMessagesCleanupTimer (ecode); });
}
void SSUData::HandleIncompleteMessagesCleanupTimer (const boost::system::error_code& ecode)
{
if (ecode != boost::asio::error::operation_aborted)
{
uint32_t ts = i2p::util::GetSecondsSinceEpoch ();
for (auto it = m_IncompleteMessages.begin (); it != m_IncompleteMessages.end ();)
{
if (ts > it->second->lastFragmentInsertTime + INCOMPLETE_MESSAGES_CLEANUP_TIMEOUT)
{
LogPrint (eLogError, "SSU message ", it->first, " was not completed in ", INCOMPLETE_MESSAGES_CLEANUP_TIMEOUT, " seconds. Deleted");
it = m_IncompleteMessages.erase (it);
}
else
it++;
}
ScheduleIncompleteMessagesCleanup ();
}
}
}
}

View File

@@ -6,6 +6,7 @@
#include <map>
#include <vector>
#include <set>
#include <memory>
#include <boost/asio.hpp>
#include "I2NPProtocol.h"
#include "Identity.h"
@@ -25,6 +26,9 @@ namespace transport
const size_t SSU_V6_MAX_PACKET_SIZE = SSU_MTU_V6 - IPV6_HEADER_SIZE - UDP_HEADER_SIZE; // 1424
const int RESEND_INTERVAL = 3; // in seconds
const int MAX_NUM_RESENDS = 5;
const int DECAY_INTERVAL = 20; // in seconds
const int MAX_NUM_RECEIVED_MESSAGES = 1000; // how many msgID we store for duplicates check
const int INCOMPLETE_MESSAGES_CLEANUP_TIMEOUT = 30; // in seconds
// data flags
const uint8_t DATA_FLAG_EXTENDED_DATA_INCLUDED = 0x02;
const uint8_t DATA_FLAG_WANT_REPLY = 0x04;
@@ -47,7 +51,7 @@ namespace transport
struct FragmentCmp
{
bool operator() (const Fragment * f1, const Fragment * f2) const
bool operator() (const std::unique_ptr<Fragment>& f1, const std::unique_ptr<Fragment>& f2) const
{
return f1->fragmentNum < f2->fragmentNum;
};
@@ -57,19 +61,18 @@ namespace transport
{
I2NPMessage * msg;
int nextFragmentNum;
std::set<Fragment *, FragmentCmp> savedFragments;
uint32_t lastFragmentInsertTime; // in seconds
std::set<std::unique_ptr<Fragment>, FragmentCmp> savedFragments;
IncompleteMessage (I2NPMessage * m): msg (m), nextFragmentNum (0) {};
~IncompleteMessage () { for (auto it: savedFragments) { delete it; }; };
IncompleteMessage (I2NPMessage * m): msg (m), nextFragmentNum (0), lastFragmentInsertTime (0) {};
~IncompleteMessage () { if (msg) DeleteI2NPMessage (msg); };
};
struct SentMessage
{
std::vector<Fragment *> fragments;
std::vector<std::unique_ptr<Fragment> > fragments;
uint32_t nextResendTime; // in seconds
int numResends;
~SentMessage () { for (auto it: fragments) { delete it; }; };
};
class SSUSession;
@@ -80,7 +83,11 @@ namespace transport
SSUData (SSUSession& session);
~SSUData ();
void Start ();
void Stop ();
void ProcessMessage (uint8_t * buf, size_t len);
void FlushReceivedMessage ();
void Send (i2p::I2NPMessage * msg);
void UpdatePacketSize (const i2p::data::IdentHash& remoteIdent);
@@ -95,17 +102,24 @@ namespace transport
void ScheduleResend ();
void HandleResendTimer (const boost::system::error_code& ecode);
void ScheduleDecay ();
void HandleDecayTimer (const boost::system::error_code& ecode);
void ScheduleIncompleteMessagesCleanup ();
void HandleIncompleteMessagesCleanupTimer (const boost::system::error_code& ecode);
void AdjustPacketSize (const i2p::data::RouterInfo& remoteRouter);
private:
SSUSession& m_Session;
std::map<uint32_t, IncompleteMessage *> m_IncomleteMessages;
std::map<uint32_t, SentMessage *> m_SentMessages;
std::map<uint32_t, std::unique_ptr<IncompleteMessage> > m_IncompleteMessages;
std::map<uint32_t, std::unique_ptr<SentMessage> > m_SentMessages;
std::set<uint32_t> m_ReceivedMessages;
boost::asio::deadline_timer m_ResendTimer;
boost::asio::deadline_timer m_ResendTimer, m_DecayTimer, m_IncompleteMessagesCleanupTimer;
int m_MaxPacketSize, m_PacketSize;
i2p::I2NPMessagesHandler m_Handler;
};
}
}

View File

@@ -15,10 +15,9 @@ namespace transport
{
SSUSession::SSUSession (SSUServer& server, boost::asio::ip::udp::endpoint& remoteEndpoint,
std::shared_ptr<const i2p::data::RouterInfo> router, bool peerTest ): TransportSession (router),
m_Server (server), m_RemoteEndpoint (remoteEndpoint),
m_Timer (m_Server.GetService ()), m_PeerTest (peerTest),
m_State (eSessionStateUnknown), m_IsSessionKey (false), m_RelayTag (0),
m_Data (*this), m_NumSentBytes (0), m_NumReceivedBytes (0)
m_Server (server), m_RemoteEndpoint (remoteEndpoint), m_Timer (GetService ()),
m_PeerTest (peerTest),m_State (eSessionStateUnknown), m_IsSessionKey (false), m_RelayTag (0),
m_NumSentBytes (0), m_NumReceivedBytes (0), m_Data (*this), m_IsDataReceived (false)
{
m_CreationTime = i2p::util::GetSecondsSinceEpoch ();
}
@@ -26,6 +25,11 @@ namespace transport
SSUSession::~SSUSession ()
{
}
boost::asio::io_service& SSUSession::GetService ()
{
return IsV6 () ? m_Server.GetServiceV6 () : m_Server.GetService ();
}
void SSUSession::CreateAESandMacKey (const uint8_t * pubKey)
{
@@ -126,7 +130,6 @@ namespace transport
switch (header->GetPayloadType ())
{
case PAYLOAD_TYPE_DATA:
LogPrint (eLogDebug, "SSU data received");
ProcessData (buf + sizeof (SSUHeader), len - sizeof (SSUHeader));
break;
case PAYLOAD_TYPE_SESSION_REQUEST:
@@ -255,7 +258,7 @@ namespace transport
if (paddingSize > 0) paddingSize = 16 - paddingSize;
payload += paddingSize;
// TODO: verify signature (need data from session request), payload points to signature
SendI2NPMessage (CreateDeliveryStatusMsg (0));
m_Data.Send (CreateDeliveryStatusMsg (0));
Established ();
}
@@ -755,15 +758,18 @@ namespace transport
void SSUSession::Close ()
{
m_State = eSessionStateClosed;
SendSesionDestroyed ();
if (!m_DelayedMessages.empty ())
{
for (auto it :m_DelayedMessages)
DeleteI2NPMessage (it);
m_DelayedMessages.clear ();
}
transports.PeerDisconnected (shared_from_this ());
m_Data.Stop ();
m_Timer.cancel ();
}
void SSUSession::Done ()
{
GetService ().post (std::bind (&SSUSession::Failed, shared_from_this ()));
}
void SSUSession::Established ()
{
m_State = eSessionStateEstablished;
@@ -772,13 +778,9 @@ namespace transport
delete m_DHKeysPair;
m_DHKeysPair = nullptr;
}
SendI2NPMessage (CreateDatabaseStoreMsg ());
if (!m_DelayedMessages.empty ())
{
for (auto it :m_DelayedMessages)
m_Data.Send (it);
m_DelayedMessages.clear ();
}
m_Data.Start ();
m_Data.Send (CreateDatabaseStoreMsg ());
transports.PeerConnected (shared_from_this ());
if (m_PeerTest && (m_RemoteRouter && m_RemoteRouter->IsPeerTesting ()))
SendPeerTest ();
ScheduleTermination ();
@@ -828,25 +830,53 @@ namespace transport
void SSUSession::SendI2NPMessage (I2NPMessage * msg)
{
m_Server.GetService ().post (std::bind (&SSUSession::PostI2NPMessage, shared_from_this (), msg));
GetService ().post (std::bind (&SSUSession::PostI2NPMessage, shared_from_this (), msg));
}
void SSUSession::PostI2NPMessage (I2NPMessage * msg)
{
if (msg)
{
{
if (m_State == eSessionStateEstablished)
m_Data.Send (msg);
else
m_DelayedMessages.push_back (msg);
}
DeleteI2NPMessage (msg);
}
}
void SSUSession::SendI2NPMessages (const std::vector<I2NPMessage *>& msgs)
{
GetService ().post (std::bind (&SSUSession::PostI2NPMessages, shared_from_this (), msgs));
}
void SSUSession::PostI2NPMessages (std::vector<I2NPMessage *> msgs)
{
if (m_State == eSessionStateEstablished)
{
for (auto it: msgs)
if (it) m_Data.Send (it);
}
else
{
for (auto it: msgs)
DeleteI2NPMessage (it);
}
}
void SSUSession::ProcessData (uint8_t * buf, size_t len)
{
m_Data.ProcessMessage (buf, len);
m_IsDataReceived = true;
}
void SSUSession::FlushData ()
{
if (m_IsDataReceived)
{
m_Data.FlushReceivedMessage ();
m_IsDataReceived = false;
}
}
void SSUSession::ProcessPeerTest (uint8_t * buf, size_t len, const boost::asio::ip::udp::endpoint& senderEndpoint)
{

View File

@@ -3,7 +3,6 @@
#include <inttypes.h>
#include <set>
#include <list>
#include <memory>
#include "aes.h"
#include "hmac.h"
@@ -46,6 +45,7 @@ namespace transport
eSessionStateUnknown,
eSessionStateIntroduced,
eSessionStateEstablished,
eSessionStateClosed,
eSessionStateFailed
};
@@ -64,9 +64,11 @@ namespace transport
void Introduce (uint32_t iTag, const uint8_t * iKey);
void WaitForIntroduction ();
void Close ();
void Done ();
boost::asio::ip::udp::endpoint& GetRemoteEndpoint () { return m_RemoteEndpoint; };
bool IsV6 () const { return m_RemoteEndpoint.address ().is_v6 (); };
void SendI2NPMessage (I2NPMessage * msg);
void SendI2NPMessages (const std::vector<I2NPMessage *>& msgs);
void SendPeerTest (); // Alice
SessionState GetState () const { return m_State; };
@@ -77,11 +79,15 @@ namespace transport
uint32_t GetRelayTag () const { return m_RelayTag; };
uint32_t GetCreationTime () const { return m_CreationTime; };
void FlushData ();
private:
boost::asio::io_service& GetService ();
void CreateAESandMacKey (const uint8_t * pubKey);
void PostI2NPMessage (I2NPMessage * msg);
void PostI2NPMessages (std::vector<I2NPMessage *> msgs);
void ProcessMessage (uint8_t * buf, size_t len, const boost::asio::ip::udp::endpoint& senderEndpoint); // call for established session
void ProcessSessionRequest (uint8_t * buf, size_t len, const boost::asio::ip::udp::endpoint& senderEndpoint);
void SendSessionRequest ();
@@ -132,10 +138,10 @@ namespace transport
i2p::crypto::CBCDecryption m_SessionKeyDecryption;
i2p::crypto::AESKey m_SessionKey;
i2p::crypto::MACKey m_MacKey;
std::list<i2p::I2NPMessage *> m_DelayedMessages;
SSUData m_Data;
size_t m_NumSentBytes, m_NumReceivedBytes;
uint32_t m_CreationTime; // seconds since epoch
SSUData m_Data;
bool m_IsDataReceived;
};

View File

@@ -12,12 +12,12 @@ namespace i2p
namespace stream
{
Stream::Stream (boost::asio::io_service& service, StreamingDestination& local,
const i2p::data::LeaseSet& remote, int port): m_Service (service), m_SendStreamID (0),
std::shared_ptr<const i2p::data::LeaseSet> remote, int port): m_Service (service), m_SendStreamID (0),
m_SequenceNumber (0), m_LastReceivedSequenceNumber (-1), m_IsOpen (false),
m_IsReset (false), m_IsAckSendScheduled (false), m_LocalDestination (local),
m_RemoteLeaseSet (&remote), m_RoutingSession (nullptr), m_CurrentOutboundTunnel (nullptr),
m_ReceiveTimer (m_Service), m_ResendTimer (m_Service), m_AckSendTimer (m_Service),
m_NumSentBytes (0), m_NumReceivedBytes (0), m_Port (port)
m_RemoteLeaseSet (remote), m_ReceiveTimer (m_Service), m_ResendTimer (m_Service),
m_AckSendTimer (m_Service), m_NumSentBytes (0), m_NumReceivedBytes (0), m_Port (port),
m_WindowSize (MIN_WINDOW_SIZE), m_RTT (INITIAL_RTT), m_LastWindowSizeIncreaseTime (0)
{
m_RecvStreamID = i2p::context.GetRandomNumberGenerator ().GenerateWord32 ();
UpdateCurrentRemoteLease ();
@@ -26,9 +26,9 @@ namespace stream
Stream::Stream (boost::asio::io_service& service, StreamingDestination& local):
m_Service (service), m_SendStreamID (0), m_SequenceNumber (0), m_LastReceivedSequenceNumber (-1),
m_IsOpen (false), m_IsReset (false), m_IsAckSendScheduled (false), m_LocalDestination (local),
m_RemoteLeaseSet (nullptr), m_RoutingSession (nullptr), m_CurrentOutboundTunnel (nullptr),
m_ReceiveTimer (m_Service), m_ResendTimer (m_Service), m_AckSendTimer (m_Service),
m_NumSentBytes (0), m_NumReceivedBytes (0), m_Port (0)
m_NumSentBytes (0), m_NumReceivedBytes (0), m_Port (0), m_WindowSize (MIN_WINDOW_SIZE),
m_RTT (INITIAL_RTT), m_LastWindowSizeIncreaseTime (0)
{
m_RecvStreamID = i2p::context.GetRandomNumberGenerator ().GenerateWord32 ();
}
@@ -113,10 +113,8 @@ namespace stream
{
if (receivedSeqn <= m_LastReceivedSequenceNumber)
{
// we have received duplicate. Most likely our outbound tunnel is dead
// we have received duplicate
LogPrint (eLogWarning, "Duplicate message ", receivedSeqn, " received");
m_CurrentOutboundTunnel = nullptr; // pick another outbound tunnel
UpdateCurrentRemoteLease (); // pick another lease
SendQuickAck (); // resend ack for previous message again
delete packet; // packet dropped
}
@@ -211,6 +209,8 @@ namespace stream
void Stream::ProcessAck (Packet * packet)
{
bool acknowledged = false;
auto ts = i2p::util::GetMillisecondsSinceEpoch ();
uint32_t ackThrough = packet->GetAckThrough ();
int nackCount = packet->GetNACKCount ();
for (auto it = m_SentPackets.begin (); it != m_SentPackets.end ();)
@@ -235,92 +235,131 @@ namespace stream
}
}
auto sentPacket = *it;
LogPrint (eLogDebug, "Packet ", seqn, " acknowledged");
uint64_t rtt = ts - sentPacket->sendTime;
m_RTT = (m_RTT*seqn + rtt)/(seqn + 1);
LogPrint (eLogDebug, "Packet ", seqn, " acknowledged rtt=", rtt);
m_SentPackets.erase (it++);
delete sentPacket;
acknowledged = true;
if (m_WindowSize < WINDOW_SIZE)
m_WindowSize++; // slow start
else
{
// linear growth
if (ts > m_LastWindowSizeIncreaseTime + m_RTT)
{
m_WindowSize++;
if (m_WindowSize > MAX_WINDOW_SIZE) m_WindowSize = MAX_WINDOW_SIZE;
m_LastWindowSizeIncreaseTime = ts;
}
}
}
else
break;
}
if (m_SentPackets.empty ())
m_ResendTimer.cancel ();
if (acknowledged)
SendBuffer ();
}
size_t Stream::Send (const uint8_t * buf, size_t len)
{
bool isNoAck = m_LastReceivedSequenceNumber < 0; // first packet
std::vector<Packet *> packets;
while (!m_IsOpen || len > 0)
if (len > 0 && buf)
{
Packet * p = new Packet ();
uint8_t * packet = p->GetBuffer ();
// TODO: implement setters
size_t size = 0;
htobe32buf (packet + size, m_SendStreamID);
size += 4; // sendStreamID
htobe32buf (packet + size, m_RecvStreamID);
size += 4; // receiveStreamID
htobe32buf (packet + size, m_SequenceNumber++);
size += 4; // sequenceNum
if (isNoAck)
htobe32buf (packet + size, m_LastReceivedSequenceNumber);
else
htobuf32 (packet + size, 0);
size += 4; // ack Through
packet[size] = 0;
size++; // NACK count
packet[size] = RESEND_TIMEOUT;
size++; // resend delay
if (!m_IsOpen)
{
// initial packet
m_IsOpen = true; m_IsReset = false;
uint16_t flags = PACKET_FLAG_SYNCHRONIZE | PACKET_FLAG_FROM_INCLUDED |
PACKET_FLAG_SIGNATURE_INCLUDED | PACKET_FLAG_MAX_PACKET_SIZE_INCLUDED;
if (isNoAck) flags |= PACKET_FLAG_NO_ACK;
htobe16buf (packet + size, flags);
size += 2; // flags
size_t identityLen = m_LocalDestination.GetOwner ().GetIdentity ().GetFullLen ();
size_t signatureLen = m_LocalDestination.GetOwner ().GetIdentity ().GetSignatureLen ();
htobe16buf (packet + size, identityLen + signatureLen + 2); // identity + signature + packet size
size += 2; // options size
m_LocalDestination.GetOwner ().GetIdentity ().ToBuffer (packet + size, identityLen);
size += identityLen; // from
htobe16buf (packet + size, STREAMING_MTU);
size += 2; // max packet size
uint8_t * signature = packet + size; // set it later
memset (signature, 0, signatureLen); // zeroes for now
size += signatureLen; // signature
size_t sentLen = STREAMING_MTU - size;
if (len < sentLen) sentLen = len;
memcpy (packet + size, buf, sentLen);
buf += sentLen;
len -= sentLen;
size += sentLen; // payload
m_LocalDestination.GetOwner ().Sign (packet, size, signature);
}
else
{
// follow on packet
htobuf16 (packet + size, 0);
size += 2; // flags
htobuf16 (packet + size, 0); // no options
size += 2; // options size
size_t sentLen = STREAMING_MTU - size;
if (len < sentLen) sentLen = len;
memcpy (packet + size, buf, sentLen);
buf += sentLen;
len -= sentLen;
size += sentLen; // payload
}
p->len = size;
packets.push_back (p);
}
if (packets.size () > 0)
m_Service.post (std::bind (&Stream::PostPackets, shared_from_this (), packets));
std::unique_lock<std::mutex> l(m_SendBufferMutex);
m_SendBuffer.clear ();
m_SendBuffer.write ((const char *)buf, len);
}
m_Service.post (std::bind (&Stream::SendBuffer, shared_from_this ()));
return len;
}
void Stream::SendBuffer ()
{
int numMsgs = m_WindowSize - m_SentPackets.size ();
if (numMsgs <= 0) return; // window is full
bool isNoAck = m_LastReceivedSequenceNumber < 0; // first packet
std::vector<Packet *> packets;
{
std::unique_lock<std::mutex> l(m_SendBufferMutex);
while (!m_IsOpen || (IsEstablished () && !m_SendBuffer.eof () && numMsgs > 0))
{
Packet * p = new Packet ();
uint8_t * packet = p->GetBuffer ();
// TODO: implement setters
size_t size = 0;
htobe32buf (packet + size, m_SendStreamID);
size += 4; // sendStreamID
htobe32buf (packet + size, m_RecvStreamID);
size += 4; // receiveStreamID
htobe32buf (packet + size, m_SequenceNumber++);
size += 4; // sequenceNum
if (isNoAck)
htobe32buf (packet + size, m_LastReceivedSequenceNumber);
else
htobuf32 (packet + size, 0);
size += 4; // ack Through
packet[size] = 0;
size++; // NACK count
packet[size] = RESEND_TIMEOUT;
size++; // resend delay
if (!m_IsOpen)
{
// initial packet
m_IsOpen = true; m_IsReset = false;
uint16_t flags = PACKET_FLAG_SYNCHRONIZE | PACKET_FLAG_FROM_INCLUDED |
PACKET_FLAG_SIGNATURE_INCLUDED | PACKET_FLAG_MAX_PACKET_SIZE_INCLUDED;
if (isNoAck) flags |= PACKET_FLAG_NO_ACK;
htobe16buf (packet + size, flags);
size += 2; // flags
size_t identityLen = m_LocalDestination.GetOwner ().GetIdentity ().GetFullLen ();
size_t signatureLen = m_LocalDestination.GetOwner ().GetIdentity ().GetSignatureLen ();
htobe16buf (packet + size, identityLen + signatureLen + 2); // identity + signature + packet size
size += 2; // options size
m_LocalDestination.GetOwner ().GetIdentity ().ToBuffer (packet + size, identityLen);
size += identityLen; // from
htobe16buf (packet + size, STREAMING_MTU);
size += 2; // max packet size
uint8_t * signature = packet + size; // set it later
memset (signature, 0, signatureLen); // zeroes for now
size += signatureLen; // signature
m_SendBuffer.read ((char *)(packet + size), STREAMING_MTU - size);
size += m_SendBuffer.gcount (); // payload
m_LocalDestination.GetOwner ().Sign (packet, size, signature);
}
else
{
// follow on packet
htobuf16 (packet + size, 0);
size += 2; // flags
htobuf16 (packet + size, 0); // no options
size += 2; // options size
m_SendBuffer.read((char *)(packet + size), STREAMING_MTU - size);
size += m_SendBuffer.gcount (); // payload
}
p->len = size;
packets.push_back (p);
numMsgs--;
}
}
if (packets.size () > 0)
{
m_IsAckSendScheduled = false;
m_AckSendTimer.cancel ();
bool isEmpty = m_SentPackets.empty ();
auto ts = i2p::util::GetMillisecondsSinceEpoch ();
for (auto it: packets)
{
it->sendTime = ts;
m_SentPackets.insert (it);
}
SendPackets (packets);
if (isEmpty)
ScheduleResend ();
}
}
void Stream::SendQuickAck ()
{
@@ -356,6 +395,12 @@ namespace stream
for (auto it: m_SavedPackets)
{
auto seqn = it->GetSeqn ();
if (numNacks + (seqn - nextSeqn) >= 256)
{
LogPrint (eLogError, "Number of NACKs exceeds 256");
htobe32buf (packet + 12, nextSeqn); // change ack Through
break;
}
for (uint32_t i = nextSeqn; i < seqn; i++)
{
htobe32buf (nacks, i);
@@ -417,9 +462,9 @@ namespace stream
p->len = size;
m_Service.post (std::bind (&Stream::SendPacket, shared_from_this (), p));
LogPrint ("FIN sent");
m_ReceiveTimer.cancel ();
m_LocalDestination.DeleteStream (shared_from_this ());
}
m_ReceiveTimer.cancel ();
m_LocalDestination.DeleteStream (shared_from_this ());
}
size_t Stream::ConcatenatePackets (uint8_t * buf, size_t len)
@@ -465,30 +510,6 @@ namespace stream
else
return false;
}
void Stream::PostPackets (const std::vector<Packet *> packets)
{
if (m_IsOpen)
{
if (packets.size () > 0)
{
m_IsAckSendScheduled = false;
m_AckSendTimer.cancel ();
}
bool isEmpty = m_SentPackets.empty ();
for (auto it: packets)
m_SentPackets.insert (it);
SendPackets (packets);
if (isEmpty)
ScheduleResend ();
}
else
{
// delete
for (auto it: packets)
delete it;
}
}
void Stream::SendPackets (const std::vector<Packet *>& packets)
{
@@ -501,7 +522,8 @@ namespace stream
return;
}
}
m_CurrentOutboundTunnel = m_LocalDestination.GetOwner ().GetTunnelPool ()->GetNextOutboundTunnel (m_CurrentOutboundTunnel);
if (!m_CurrentOutboundTunnel || !m_CurrentOutboundTunnel->IsEstablished ())
m_CurrentOutboundTunnel = m_LocalDestination.GetOwner ().GetTunnelPool ()->GetNextOutboundTunnel ();
if (!m_CurrentOutboundTunnel)
{
LogPrint ("No outbound tunnels in the pool");
@@ -543,15 +565,23 @@ namespace stream
{
if (ecode != boost::asio::error::operation_aborted)
{
auto ts = i2p::util::GetMillisecondsSinceEpoch ();
bool congesion = false, first = true;
std::vector<Packet *> packets;
for (auto it : m_SentPackets)
{
it->numResendAttempts++;
if (first && it->numResendAttempts == 1) // detect congesion at first attempt of first packet only
congesion = true;
first = false;
if (it->numResendAttempts <= MAX_NUM_RESEND_ATTEMPTS)
{
it->sendTime = ts;
packets.push_back (it);
}
else
{
LogPrint (eLogWarning, "Packet ", it->GetSeqn (), "was not ACKed after ", MAX_NUM_RESEND_ATTEMPTS, " attempts. Terminate");
LogPrint (eLogWarning, "Packet ", it->GetSeqn (), " was not ACKed after ", MAX_NUM_RESEND_ATTEMPTS, " attempts. Terminate");
m_IsOpen = false;
m_IsReset = true;
m_ReceiveTimer.cancel ();
@@ -560,8 +590,18 @@ namespace stream
}
if (packets.size () > 0)
{
m_CurrentOutboundTunnel = nullptr; // pick another outbound tunnel
UpdateCurrentRemoteLease (); // pick another lease
if (congesion)
{
// congesion avoidance
m_WindowSize /= 2;
if (m_WindowSize < MIN_WINDOW_SIZE) m_WindowSize = MIN_WINDOW_SIZE;
}
else
{
// congesion avoidance didn't help
m_CurrentOutboundTunnel = nullptr; // pick another outbound tunnel
UpdateCurrentRemoteLease (); // pick another lease
}
SendPackets (packets);
}
ScheduleResend ();
@@ -589,7 +629,7 @@ namespace stream
if (m_RemoteLeaseSet)
{
if (!m_RoutingSession)
m_RoutingSession = m_LocalDestination.GetOwner ().GetRoutingSession (*m_RemoteLeaseSet, 32);
m_RoutingSession = m_LocalDestination.GetOwner ().GetRoutingSession (m_RemoteLeaseSet, 32);
auto leases = m_RemoteLeaseSet->GetNonExpiredLeases ();
if (!leases.empty ())
{
@@ -688,7 +728,7 @@ namespace stream
}
}
std::shared_ptr<Stream> StreamingDestination::CreateNewOutgoingStream (const i2p::data::LeaseSet& remote, int port)
std::shared_ptr<Stream> StreamingDestination::CreateNewOutgoingStream (std::shared_ptr<const i2p::data::LeaseSet> remote, int port)
{
auto s = std::make_shared<Stream> (m_Owner.GetService (), *this, remote, port);
std::unique_lock<std::mutex> l(m_StreamsMutex);

View File

@@ -3,11 +3,13 @@
#include <inttypes.h>
#include <string>
#include <sstream>
#include <map>
#include <set>
#include <queue>
#include <functional>
#include <memory>
#include <mutex>
#include <boost/asio.hpp>
#include "I2PEndian.h"
#include "Identity.h"
@@ -42,14 +44,19 @@ namespace stream
const int RESEND_TIMEOUT = 10; // in seconds
const int ACK_SEND_TIMEOUT = 200; // in milliseconds
const int MAX_NUM_RESEND_ATTEMPTS = 5;
const int WINDOW_SIZE = 6; // in messages
const int MIN_WINDOW_SIZE = 1;
const int MAX_WINDOW_SIZE = 128;
const int INITIAL_RTT = 8000; // in milliseconds
struct Packet
{
size_t len, offset;
uint8_t buf[MAX_PACKET_SIZE];
int numResendAttempts;
uint64_t sendTime;
Packet (): len (0), offset (0), numResendAttempts (0) {};
Packet (): len (0), offset (0), numResendAttempts (0), sendTime (0) {};
uint8_t * GetBuffer () { return buf + offset; };
size_t GetLength () const { return len - offset; };
@@ -83,13 +90,13 @@ namespace stream
public:
Stream (boost::asio::io_service& service, StreamingDestination& local,
const i2p::data::LeaseSet& remote, int port = 0); // outgoing
std::shared_ptr<const i2p::data::LeaseSet> remote, int port = 0); // outgoing
Stream (boost::asio::io_service& service, StreamingDestination& local); // incoming
~Stream ();
uint32_t GetSendStreamID () const { return m_SendStreamID; };
uint32_t GetRecvStreamID () const { return m_RecvStreamID; };
const i2p::data::LeaseSet * GetRemoteLeaseSet () const { return m_RemoteLeaseSet; };
std::shared_ptr<const i2p::data::LeaseSet> GetRemoteLeaseSet () const { return m_RemoteLeaseSet; };
const i2p::data::IdentityEx& GetRemoteIdentity () const { return m_RemoteIdentity; };
bool IsOpen () const { return m_IsOpen; };
bool IsEstablished () const { return m_SendStreamID; };
@@ -109,12 +116,15 @@ namespace stream
size_t GetNumReceivedBytes () const { return m_NumReceivedBytes; };
size_t GetSendQueueSize () const { return m_SentPackets.size (); };
size_t GetReceiveQueueSize () const { return m_ReceiveQueue.size (); };
size_t GetSendBufferSize () const { return m_SendBuffer.rdbuf ()->in_avail (); };
int GetWindowSize () const { return m_WindowSize; };
int GetRTT () const { return m_RTT; };
private:
void SendBuffer ();
void SendQuickAck ();
bool SendPacket (Packet * packet);
void PostPackets (const std::vector<Packet *> packets);
void SendPackets (const std::vector<Packet *>& packets);
void SavePacket (Packet * packet);
@@ -141,16 +151,21 @@ namespace stream
bool m_IsOpen, m_IsReset, m_IsAckSendScheduled;
StreamingDestination& m_LocalDestination;
i2p::data::IdentityEx m_RemoteIdentity;
const i2p::data::LeaseSet * m_RemoteLeaseSet;
i2p::garlic::GarlicRoutingSession * m_RoutingSession;
std::shared_ptr<const i2p::data::LeaseSet> m_RemoteLeaseSet;
std::shared_ptr<i2p::garlic::GarlicRoutingSession> m_RoutingSession;
i2p::data::Lease m_CurrentRemoteLease;
i2p::tunnel::OutboundTunnel * m_CurrentOutboundTunnel;
std::shared_ptr<i2p::tunnel::OutboundTunnel> m_CurrentOutboundTunnel;
std::queue<Packet *> m_ReceiveQueue;
std::set<Packet *, PacketCmp> m_SavedPackets;
std::set<Packet *, PacketCmp> m_SentPackets;
boost::asio::deadline_timer m_ReceiveTimer, m_ResendTimer, m_AckSendTimer;
size_t m_NumSentBytes, m_NumReceivedBytes;
uint16_t m_Port;
std::mutex m_SendBufferMutex;
std::stringstream m_SendBuffer;
int m_WindowSize, m_RTT;
uint64_t m_LastWindowSizeIncreaseTime;
};
class StreamingDestination
@@ -165,10 +180,10 @@ namespace stream
void Start ();
void Stop ();
std::shared_ptr<Stream> CreateNewOutgoingStream (const i2p::data::LeaseSet& remote, int port = 0);
std::shared_ptr<Stream> CreateNewOutgoingStream (std::shared_ptr<const i2p::data::LeaseSet> remote, int port = 0);
void DeleteStream (std::shared_ptr<Stream> stream);
void SetAcceptor (const Acceptor& acceptor) { m_Acceptor = acceptor; };
void ResetAcceptor () { m_Acceptor = nullptr; };
void ResetAcceptor () { if (m_Acceptor) m_Acceptor (nullptr); m_Acceptor = nullptr; };
bool IsAcceptorSet () const { return m_Acceptor != nullptr; };
i2p::client::ClientDestination& GetOwner () { return m_Owner; };
@@ -224,9 +239,19 @@ namespace stream
// no error
handler (boost::system::error_code (), received);
else
// socket closed
handler (m_IsReset ? boost::asio::error::make_error_code (boost::asio::error::connection_reset) :
boost::asio::error::make_error_code (boost::asio::error::operation_aborted), received);
{
// stream closed
if (m_IsReset)
{
// stream closed by peer
handler (received > 0 ? boost::system::error_code () : // we still have some data
boost::asio::error::make_error_code (boost::asio::error::connection_reset), // no more data
received);
}
else // stream closed by us
handler (boost::asio::error::make_error_code (boost::asio::error::operation_aborted), received);
}
}
else
// timeout expired

View File

@@ -15,7 +15,7 @@ namespace tunnel
const uint8_t * nextIdent, uint32_t nextTunnelID,
const uint8_t * layerKey,const uint8_t * ivKey):
m_TunnelID (receiveTunnelID), m_NextTunnelID (nextTunnelID),
m_NextIdent (nextIdent), m_NumTransmittedBytes (0)
m_NextIdent (nextIdent)
{
m_Encryption.SetKeys (layerKey, ivKey);
}
@@ -24,39 +24,65 @@ namespace tunnel
{
m_Encryption.Encrypt (tunnelMsg->GetPayload () + 4);
}
void TransitTunnel::HandleTunnelDataMsg (i2p::I2NPMessage * tunnelMsg)
TransitTunnelParticipant::~TransitTunnelParticipant ()
{
for (auto it: m_TunnelDataMsgs)
i2p::DeleteI2NPMessage (it);
}
void TransitTunnelParticipant::HandleTunnelDataMsg (i2p::I2NPMessage * tunnelMsg)
{
EncryptTunnelMsg (tunnelMsg);
LogPrint ("TransitTunnel: ",m_TunnelID,"->", m_NextTunnelID);
m_NumTransmittedBytes += tunnelMsg->GetLength ();
htobe32buf (tunnelMsg->GetPayload (), m_NextTunnelID);
htobe32buf (tunnelMsg->GetPayload (), GetNextTunnelID ());
FillI2NPMessageHeader (tunnelMsg, eI2NPTunnelData);
i2p::transport::transports.SendMessage (m_NextIdent, tunnelMsg);
m_TunnelDataMsgs.push_back (tunnelMsg);
}
void TransitTunnelParticipant::FlushTunnelDataMsgs ()
{
if (!m_TunnelDataMsgs.empty ())
{
LogPrint (eLogDebug, "TransitTunnel: ",GetTunnelID (),"->", GetNextTunnelID (), " ", m_TunnelDataMsgs.size ());
i2p::transport::transports.SendMessages (GetNextIdentHash (), m_TunnelDataMsgs);
m_TunnelDataMsgs.clear ();
}
}
void TransitTunnel::SendTunnelDataMsg (i2p::I2NPMessage * msg)
{
LogPrint ("We are not a gateway for transit tunnel ", m_TunnelID);
LogPrint (eLogError, "We are not a gateway for transit tunnel ", m_TunnelID);
i2p::DeleteI2NPMessage (msg);
}
void TransitTunnel::HandleTunnelDataMsg (i2p::I2NPMessage * tunnelMsg)
{
LogPrint (eLogError, "Incoming tunnel message is not supported ", m_TunnelID);
DeleteI2NPMessage (tunnelMsg);
}
void TransitTunnelGateway::SendTunnelDataMsg (i2p::I2NPMessage * msg)
{
TunnelMessageBlock block;
block.deliveryType = eDeliveryTypeLocal;
block.data = msg;
std::unique_lock<std::mutex> l(m_SendMutex);
m_Gateway.SendTunnelDataMsg (block);
m_Gateway.PutTunnelDataMsg (block);
}
void TransitTunnelGateway::FlushTunnelDataMsgs ()
{
std::unique_lock<std::mutex> l(m_SendMutex);
m_Gateway.SendBuffer ();
}
void TransitTunnelEndpoint::HandleTunnelDataMsg (i2p::I2NPMessage * tunnelMsg)
{
EncryptTunnelMsg (tunnelMsg);
LogPrint ("TransitTunnel endpoint for ", GetTunnelID ());
LogPrint (eLogDebug, "TransitTunnel endpoint for ", GetTunnelID ());
m_Endpoint.HandleDecryptedTunnelDataMsg (tunnelMsg);
}
@@ -67,18 +93,18 @@ namespace tunnel
{
if (isEndpoint)
{
LogPrint ("TransitTunnel endpoint: ", receiveTunnelID, " created");
LogPrint (eLogInfo, "TransitTunnel endpoint: ", receiveTunnelID, " created");
return new TransitTunnelEndpoint (receiveTunnelID, nextIdent, nextTunnelID, layerKey, ivKey);
}
else if (isGateway)
{
LogPrint ("TransitTunnel gateway: ", receiveTunnelID, " created");
LogPrint (eLogInfo, "TransitTunnel gateway: ", receiveTunnelID, " created");
return new TransitTunnelGateway (receiveTunnelID, nextIdent, nextTunnelID, layerKey, ivKey);
}
else
{
LogPrint ("TransitTunnel: ", receiveTunnelID, "->", nextTunnelID, " created");
return new TransitTunnel (receiveTunnelID, nextIdent, nextTunnelID, layerKey, ivKey);
LogPrint (eLogInfo, "TransitTunnel: ", receiveTunnelID, "->", nextTunnelID, " created");
return new TransitTunnelParticipant (receiveTunnelID, nextIdent, nextTunnelID, layerKey, ivKey);
}
}
}

View File

@@ -2,6 +2,7 @@
#define TRANSIT_TUNNEL_H__
#include <inttypes.h>
#include <vector>
#include <mutex>
#include "aes.h"
#include "I2NPProtocol.h"
@@ -13,7 +14,7 @@ namespace i2p
{
namespace tunnel
{
class TransitTunnel: public TunnelBase // tunnel patricipant
class TransitTunnel: public TunnelBase
{
public:
@@ -21,13 +22,13 @@ namespace tunnel
const uint8_t * nextIdent, uint32_t nextTunnelID,
const uint8_t * layerKey,const uint8_t * ivKey);
virtual void HandleTunnelDataMsg (i2p::I2NPMessage * tunnelMsg);
virtual void SendTunnelDataMsg (i2p::I2NPMessage * msg);
virtual size_t GetNumTransmittedBytes () const { return m_NumTransmittedBytes; };
virtual size_t GetNumTransmittedBytes () const { return 0; };
uint32_t GetTunnelID () const { return m_TunnelID; };
// implements TunnelBase
void SendTunnelDataMsg (i2p::I2NPMessage * msg);
void HandleTunnelDataMsg (i2p::I2NPMessage * tunnelMsg);
void EncryptTunnelMsg (I2NPMessage * tunnelMsg);
uint32_t GetNextTunnelID () const { return m_NextTunnelID; };
const i2p::data::IdentHash& GetNextIdentHash () const { return m_NextIdent; };
@@ -36,11 +37,31 @@ namespace tunnel
uint32_t m_TunnelID, m_NextTunnelID;
i2p::data::IdentHash m_NextIdent;
size_t m_NumTransmittedBytes;
i2p::crypto::TunnelEncryption m_Encryption;
};
class TransitTunnelParticipant: public TransitTunnel
{
public:
TransitTunnelParticipant (uint32_t receiveTunnelID,
const uint8_t * nextIdent, uint32_t nextTunnelID,
const uint8_t * layerKey,const uint8_t * ivKey):
TransitTunnel (receiveTunnelID, nextIdent, nextTunnelID,
layerKey, ivKey), m_NumTransmittedBytes (0) {};
~TransitTunnelParticipant ();
size_t GetNumTransmittedBytes () const { return m_NumTransmittedBytes; };
void HandleTunnelDataMsg (i2p::I2NPMessage * tunnelMsg);
void FlushTunnelDataMsgs ();
private:
size_t m_NumTransmittedBytes;
std::vector<i2p::I2NPMessage *> m_TunnelDataMsgs;
};
class TransitTunnelGateway: public TransitTunnel
{
public:
@@ -52,6 +73,7 @@ namespace tunnel
layerKey, ivKey), m_Gateway(this) {};
void SendTunnelDataMsg (i2p::I2NPMessage * msg);
void FlushTunnelDataMsgs ();
size_t GetNumTransmittedBytes () const { return m_Gateway.GetNumSentBytes (); };
private:

View File

@@ -4,8 +4,10 @@
#include <inttypes.h>
#include <iostream>
#include <memory>
#include <vector>
#include "Identity.h"
#include "RouterInfo.h"
#include "I2NPProtocol.h"
namespace i2p
{
@@ -60,10 +62,14 @@ namespace transport
}
virtual ~TransportSession () { delete m_DHKeysPair; };
virtual void Done () = 0;
std::shared_ptr<const i2p::data::RouterInfo> GetRemoteRouter () { return m_RemoteRouter; };
const i2p::data::IdentityEx& GetRemoteIdentity () { return m_RemoteIdentity; };
virtual void SendI2NPMessage (I2NPMessage * msg) = 0;
virtual void SendI2NPMessages (const std::vector<I2NPMessage *>& msgs) = 0;
protected:
std::shared_ptr<const i2p::data::RouterInfo> m_RemoteRouter;

View File

@@ -1,5 +1,4 @@
#include <cryptopp/dh.h>
#include <boost/bind.hpp>
#include "Log.h"
#include "CryptoConst.h"
#include "RouterContext.h"
@@ -96,8 +95,9 @@ namespace transport
Transports transports;
Transports::Transports ():
m_Thread (nullptr), m_Work (m_Service), m_NTCPAcceptor (nullptr), m_NTCPV6Acceptor (nullptr),
m_SSUServer (nullptr), m_DHKeysPairSupplier (5) // 5 pre-generated keys
m_IsRunning (false), m_Thread (nullptr), m_Work (m_Service), m_PeerCleanupTimer (m_Service),
m_NTCPServer (nullptr), m_SSUServer (nullptr),
m_DHKeysPairSupplier (5) // 5 pre-generated keys
{
}
@@ -115,31 +115,13 @@ namespace transport
auto addresses = context.GetRouterInfo ().GetAddresses ();
for (auto& address : addresses)
{
if (address.transportStyle == RouterInfo::eTransportNTCP && address.host.is_v4 ())
if (!m_NTCPServer)
{
m_NTCPAcceptor = new boost::asio::ip::tcp::acceptor (m_Service,
boost::asio::ip::tcp::endpoint(boost::asio::ip::tcp::v4(), address.port));
LogPrint ("Start listening TCP port ", address.port);
auto conn = std::make_shared<NTCPSession>(m_Service);
m_NTCPAcceptor->async_accept(conn->GetSocket (), boost::bind (&Transports::HandleAccept, this,
conn, boost::asio::placeholders::error));
if (context.SupportsV6 ())
{
m_NTCPV6Acceptor = new boost::asio::ip::tcp::acceptor (m_Service);
m_NTCPV6Acceptor->open (boost::asio::ip::tcp::v6());
m_NTCPV6Acceptor->set_option (boost::asio::ip::v6_only (true));
m_NTCPV6Acceptor->bind (boost::asio::ip::tcp::endpoint(boost::asio::ip::tcp::v6(), address.port));
m_NTCPV6Acceptor->listen ();
LogPrint ("Start listening V6 TCP port ", address.port);
auto conn = std::make_shared<NTCPSession> (m_Service);
m_NTCPV6Acceptor->async_accept(conn->GetSocket (), boost::bind (&Transports::HandleAcceptV6,
this, conn, boost::asio::placeholders::error));
}
m_NTCPServer = new NTCPServer (address.port);
m_NTCPServer->Start ();
}
else if (address.transportStyle == RouterInfo::eTransportSSU && address.host.is_v4 ())
if (address.transportStyle == RouterInfo::eTransportSSU && address.host.is_v4 ())
{
if (!m_SSUServer)
{
@@ -152,22 +134,26 @@ namespace transport
LogPrint ("SSU server already exists");
}
}
m_PeerCleanupTimer.expires_from_now (boost::posix_time::seconds(5*SESSION_CREATION_TIMEOUT));
m_PeerCleanupTimer.async_wait (std::bind (&Transports::HandlePeerCleanupTimer, this, std::placeholders::_1));
}
void Transports::Stop ()
{
m_PeerCleanupTimer.cancel ();
m_Peers.clear ();
if (m_SSUServer)
{
m_SSUServer->Stop ();
delete m_SSUServer;
m_SSUServer = nullptr;
}
m_NTCPSessions.clear ();
delete m_NTCPAcceptor;
m_NTCPAcceptor = nullptr;
delete m_NTCPV6Acceptor;
m_NTCPV6Acceptor = nullptr;
if (m_NTCPServer)
{
m_NTCPServer->Stop ();
delete m_NTCPServer;
m_NTCPServer = nullptr;
}
m_DHKeysPairSupplier.Stop ();
m_IsRunning = false;
@@ -195,175 +181,194 @@ namespace transport
}
}
void Transports::AddNTCPSession (std::shared_ptr<NTCPSession> session)
{
if (session)
m_NTCPSessions[session->GetRemoteIdentity ().GetIdentHash ()] = session;
}
void Transports::RemoveNTCPSession (std::shared_ptr<NTCPSession> session)
{
if (session)
m_NTCPSessions.erase (session->GetRemoteIdentity ().GetIdentHash ());
}
void Transports::HandleAccept (std::shared_ptr<NTCPSession> conn, const boost::system::error_code& error)
{
if (!error)
{
LogPrint ("Connected from ", conn->GetSocket ().remote_endpoint().address ().to_string ());
conn->ServerLogin ();
}
if (error != boost::asio::error::operation_aborted)
{
conn = std::make_shared<NTCPSession> (m_Service);
m_NTCPAcceptor->async_accept(conn->GetSocket (), boost::bind (&Transports::HandleAccept, this,
conn, boost::asio::placeholders::error));
}
}
void Transports::HandleAcceptV6 (std::shared_ptr<NTCPSession> conn, const boost::system::error_code& error)
{
if (!error)
{
LogPrint ("Connected from ", conn->GetSocket ().remote_endpoint().address ().to_string ());
conn->ServerLogin ();
}
if (error != boost::asio::error::operation_aborted)
{
conn = std::make_shared<NTCPSession> (m_Service);
m_NTCPV6Acceptor->async_accept(conn->GetSocket (), boost::bind (&Transports::HandleAcceptV6, this,
conn, boost::asio::placeholders::error));
}
}
void Transports::Connect (const boost::asio::ip::address& address, int port, std::shared_ptr<NTCPSession> conn)
{
LogPrint ("Connecting to ", address ,":", port);
conn->GetSocket ().async_connect (boost::asio::ip::tcp::endpoint (address, port),
boost::bind (&Transports::HandleConnect, this, boost::asio::placeholders::error, conn));
}
void Transports::HandleConnect (const boost::system::error_code& ecode, std::shared_ptr<NTCPSession> conn)
{
if (ecode)
{
LogPrint ("Connect error: ", ecode.message ());
if (ecode != boost::asio::error::operation_aborted)
{
i2p::data::netdb.SetUnreachable (conn->GetRemoteIdentity ().GetIdentHash (), true);
conn->Terminate ();
}
}
else
{
LogPrint ("Connected");
if (conn->GetSocket ().local_endpoint ().protocol () == boost::asio::ip::tcp::v6()) // ipv6
context.UpdateNTCPV6Address (conn->GetSocket ().local_endpoint ().address ());
conn->ClientLogin ();
}
}
std::shared_ptr<NTCPSession> Transports::GetNextNTCPSession ()
{
for (auto session: m_NTCPSessions)
if (session.second->IsEstablished ())
return session.second;
return 0;
}
std::shared_ptr<NTCPSession> Transports::FindNTCPSession (const i2p::data::IdentHash& ident)
{
auto it = m_NTCPSessions.find (ident);
if (it != m_NTCPSessions.end ())
return it->second;
return 0;
}
void Transports::SendMessage (const i2p::data::IdentHash& ident, i2p::I2NPMessage * msg)
{
if (ident == i2p::context.GetRouterInfo ().GetIdentHash ())
// we send it to ourself
i2p::HandleI2NPMessage (msg);
else
m_Service.post (boost::bind (&Transports::PostMessage, this, ident, msg));
m_Service.post (std::bind (&Transports::PostMessage, this, ident, msg));
}
void Transports::PostMessage (const i2p::data::IdentHash& ident, i2p::I2NPMessage * msg)
void Transports::SendMessages (const i2p::data::IdentHash& ident, const std::vector<i2p::I2NPMessage *>& msgs)
{
auto session = FindNTCPSession (ident);
if (session)
session->SendI2NPMessage (msg);
else
m_Service.post (std::bind (&Transports::PostMessages, this, ident, msgs));
}
void Transports::PostMessage (i2p::data::IdentHash ident, i2p::I2NPMessage * msg)
{
if (ident == i2p::context.GetRouterInfo ().GetIdentHash ())
{
// we send it to ourself
i2p::HandleI2NPMessage (msg);
return;
}
auto it = m_Peers.find (ident);
if (it == m_Peers.end ())
{
auto r = netdb.FindRouter (ident);
if (r)
{
auto ssuSession = m_SSUServer ? m_SSUServer->FindSession (r) : nullptr;
if (ssuSession)
ssuSession->SendI2NPMessage (msg);
else
{
// existing session not found. create new
// try NTCP first if message size < 16K
auto address = r->GetNTCPAddress (!context.SupportsV6 ());
if (address && !r->UsesIntroducer () && !r->IsUnreachable () && msg->GetLength () < NTCP_MAX_MESSAGE_SIZE)
{
auto s = std::make_shared<NTCPSession> (m_Service, r);
AddNTCPSession (s);
s->SendI2NPMessage (msg);
Connect (address->host, address->port, s);
}
else
{
// then SSU
auto s = m_SSUServer ? m_SSUServer->GetSession (r) : nullptr;
if (s)
s->SendI2NPMessage (msg);
else
it = m_Peers.insert (std::pair<i2p::data::IdentHash, Peer>(ident, { 0, r, nullptr,
i2p::util::GetSecondsSinceEpoch () })).first;
if (!ConnectToPeer (ident, it->second))
{
DeleteI2NPMessage (msg);
return;
}
}
if (it->second.session)
it->second.session->SendI2NPMessage (msg);
else
it->second.delayedMessages.push_back (msg);
}
void Transports::PostMessages (i2p::data::IdentHash ident, std::vector<i2p::I2NPMessage *> msgs)
{
if (ident == i2p::context.GetRouterInfo ().GetIdentHash ())
{
// we send it to ourself
for (auto it: msgs)
i2p::HandleI2NPMessage (it);
return;
}
auto it = m_Peers.find (ident);
if (it == m_Peers.end ())
{
auto r = netdb.FindRouter (ident);
it = m_Peers.insert (std::pair<i2p::data::IdentHash, Peer>(ident, { 0, r, nullptr,
i2p::util::GetSecondsSinceEpoch () })).first;
if (!ConnectToPeer (ident, it->second))
{
for (auto it1: msgs)
DeleteI2NPMessage (it1);
return;
}
}
if (it->second.session)
it->second.session->SendI2NPMessages (msgs);
else
{
for (auto it1: msgs)
it->second.delayedMessages.push_back (it1);
}
}
bool Transports::ConnectToPeer (const i2p::data::IdentHash& ident, Peer& peer)
{
if (peer.router) // we have RI already
{
if (!peer.numAttempts) // NTCP
{
peer.numAttempts++;
auto address = peer.router->GetNTCPAddress (!context.SupportsV6 ());
if (address)
{
#if BOOST_VERSION >= 104900
if (!address->host.is_unspecified ()) // we have address now
#else
boost::system::error_code ecode;
address->host.to_string (ecode);
if (!ecode)
#endif
{
if (!peer.router->UsesIntroducer () && !peer.router->IsUnreachable ())
{
auto s = std::make_shared<NTCPSession> (*m_NTCPServer, peer.router);
m_NTCPServer->Connect (address->host, address->port, s);
return true;
}
}
else // we don't have address
{
if (address->addressString.length () > 0) // trying to resolve
{
LogPrint ("No NTCP and SSU addresses available");
DeleteI2NPMessage (msg);
LogPrint (eLogInfo, "Resolving ", address->addressString);
NTCPResolve (address->addressString, ident);
return true;
}
}
}
}
else if (peer.numAttempts == 1)// SSU
{
peer.numAttempts++;
if (m_SSUServer)
{
if (m_SSUServer->GetSession (peer.router))
return true;
}
}
LogPrint (eLogError, "No NTCP and SSU addresses available");
if (peer.session) peer.session->Done ();
m_Peers.erase (ident);
return false;
}
else // otherwise request RI
{
LogPrint ("Router not found. Requested");
i2p::data::netdb.RequestDestination (ident, std::bind (
&Transports::RequestComplete, this, std::placeholders::_1, ident));
}
return true;
}
void Transports::RequestComplete (std::shared_ptr<const i2p::data::RouterInfo> r, const i2p::data::IdentHash& ident)
{
m_Service.post (std::bind (&Transports::HandleRequestComplete, this, r, ident));
}
void Transports::HandleRequestComplete (std::shared_ptr<const i2p::data::RouterInfo> r, const i2p::data::IdentHash& ident)
{
auto it = m_Peers.find (ident);
if (it != m_Peers.end ())
{
if (r)
{
LogPrint ("Router found. Trying to connect");
it->second.router = r;
ConnectToPeer (ident, it->second);
}
else
{
LogPrint ("Router not found. Requested");
i2p::data::netdb.RequestDestination (ident);
auto resendTimer = new boost::asio::deadline_timer (m_Service);
resendTimer->expires_from_now (boost::posix_time::seconds(5)); // 5 seconds
resendTimer->async_wait (boost::bind (&Transports::HandleResendTimer,
this, boost::asio::placeholders::error, resendTimer, ident, msg));
LogPrint ("Router not found. Failed to send messages");
m_Peers.erase (it);
}
}
}
void Transports::HandleResendTimer (const boost::system::error_code& ecode,
boost::asio::deadline_timer * timer, const i2p::data::IdentHash& ident, i2p::I2NPMessage * msg)
void Transports::NTCPResolve (const std::string& addr, const i2p::data::IdentHash& ident)
{
auto r = netdb.FindRouter (ident);
if (r)
auto resolver = std::make_shared<boost::asio::ip::tcp::resolver>(m_Service);
resolver->async_resolve (boost::asio::ip::tcp::resolver::query (addr, ""),
std::bind (&Transports::HandleNTCPResolve, this,
std::placeholders::_1, std::placeholders::_2, ident, resolver));
}
void Transports::HandleNTCPResolve (const boost::system::error_code& ecode, boost::asio::ip::tcp::resolver::iterator it,
i2p::data::IdentHash ident, std::shared_ptr<boost::asio::ip::tcp::resolver> resolver)
{
auto it1 = m_Peers.find (ident);
if (it1 != m_Peers.end ())
{
LogPrint ("Router found. Sending message");
PostMessage (ident, msg);
}
else
{
LogPrint ("Router not found. Failed to send message");
DeleteI2NPMessage (msg);
}
delete timer;
}
auto& peer = it1->second;
if (!ecode && peer.router)
{
auto address = (*it).endpoint ().address ();
LogPrint (eLogInfo, (*it).host_name (), " has been resolved to ", address);
auto addr = peer.router->GetNTCPAddress ();
if (addr)
{
auto s = std::make_shared<NTCPSession> (*m_NTCPServer, peer.router);
m_NTCPServer->Connect (address, addr->port, s);
return;
}
}
LogPrint (eLogError, "Unable to resolve NTCP address: ", ecode.message ());
m_Peers.erase (it1);
}
}
void Transports::CloseSession (std::shared_ptr<const i2p::data::RouterInfo> router)
{
if (!router) return;
m_Service.post (boost::bind (&Transports::PostCloseSession, this, router));
m_Service.post (std::bind (&Transports::PostCloseSession, this, router));
}
void Transports::PostCloseSession (std::shared_ptr<const i2p::data::RouterInfo> router)
@@ -396,6 +401,67 @@ namespace transport
{
m_DHKeysPairSupplier.Return (pair);
}
void Transports::PeerConnected (std::shared_ptr<TransportSession> session)
{
m_Service.post([session, this]()
{
auto ident = session->GetRemoteIdentity ().GetIdentHash ();
auto it = m_Peers.find (ident);
if (it != m_Peers.end ())
{
if (!it->second.session)
{
it->second.session = session;
session->SendI2NPMessages (it->second.delayedMessages);
it->second.delayedMessages.clear ();
}
else
{
LogPrint (eLogError, "Session for ", ident.ToBase64 ().substr (0, 4), " already exists");
session->Done ();
}
}
else // incoming connection
m_Peers.insert (std::make_pair (ident, Peer{ 0, nullptr, session, i2p::util::GetSecondsSinceEpoch () }));
});
}
void Transports::PeerDisconnected (std::shared_ptr<TransportSession> session)
{
m_Service.post([session, this]()
{
auto ident = session->GetRemoteIdentity ().GetIdentHash ();
auto it = m_Peers.find (ident);
if (it != m_Peers.end () && (!it->second.session || it->second.session == session))
{
if (it->second.delayedMessages.size () > 0)
ConnectToPeer (ident, it->second);
else
m_Peers.erase (it);
}
});
}
void Transports::HandlePeerCleanupTimer (const boost::system::error_code& ecode)
{
if (ecode != boost::asio::error::operation_aborted)
{
auto ts = i2p::util::GetSecondsSinceEpoch ();
for (auto it = m_Peers.begin (); it != m_Peers.end (); )
{
if (!it->second.session && ts > it->second.creationTime + SESSION_CREATION_TIMEOUT)
{
LogPrint (eLogError, "Session to peer ", it->first.ToBase64 (), " has not been created in ", SESSION_CREATION_TIMEOUT, " seconds");
it = m_Peers.erase (it);
}
else
it++;
}
m_PeerCleanupTimer.expires_from_now (boost::posix_time::seconds(5*SESSION_CREATION_TIMEOUT));
m_PeerCleanupTimer.async_wait (std::bind (&Transports::HandlePeerCleanupTimer, this, std::placeholders::_1));
}
}
}
}

View File

@@ -6,6 +6,7 @@
#include <condition_variable>
#include <functional>
#include <map>
#include <vector>
#include <queue>
#include <string>
#include <memory>
@@ -50,6 +51,22 @@ namespace transport
CryptoPP::AutoSeededRandomPool m_Rnd;
};
struct Peer
{
int numAttempts;
std::shared_ptr<const i2p::data::RouterInfo> router;
std::shared_ptr<TransportSession> session;
uint64_t creationTime;
std::vector<i2p::I2NPMessage *> delayedMessages;
~Peer ()
{
for (auto it :delayedMessages)
i2p::DeleteI2NPMessage (it);
}
};
const size_t SESSION_CREATION_TIMEOUT = 10; // in seconds
class Transports
{
public:
@@ -64,27 +81,27 @@ namespace transport
i2p::transport::DHKeysPair * GetNextDHKeysPair ();
void ReuseDHKeysPair (DHKeysPair * pair);
void AddNTCPSession (std::shared_ptr<NTCPSession> session);
void RemoveNTCPSession (std::shared_ptr<NTCPSession> session);
std::shared_ptr<NTCPSession> GetNextNTCPSession ();
std::shared_ptr<NTCPSession> FindNTCPSession (const i2p::data::IdentHash& ident);
void SendMessage (const i2p::data::IdentHash& ident, i2p::I2NPMessage * msg);
void SendMessages (const i2p::data::IdentHash& ident, const std::vector<i2p::I2NPMessage *>& msgs);
void CloseSession (std::shared_ptr<const i2p::data::RouterInfo> router);
void PeerConnected (std::shared_ptr<TransportSession> session);
void PeerDisconnected (std::shared_ptr<TransportSession> session);
private:
void Run ();
void HandleAccept (std::shared_ptr<NTCPSession> conn, const boost::system::error_code& error);
void HandleAcceptV6 (std::shared_ptr<NTCPSession> conn, const boost::system::error_code& error);
void HandleResendTimer (const boost::system::error_code& ecode, boost::asio::deadline_timer * timer,
const i2p::data::IdentHash& ident, i2p::I2NPMessage * msg);
void PostMessage (const i2p::data::IdentHash& ident, i2p::I2NPMessage * msg);
void RequestComplete (std::shared_ptr<const i2p::data::RouterInfo> r, const i2p::data::IdentHash& ident);
void HandleRequestComplete (std::shared_ptr<const i2p::data::RouterInfo> r, const i2p::data::IdentHash& ident);
void PostMessage (i2p::data::IdentHash ident, i2p::I2NPMessage * msg);
void PostMessages (i2p::data::IdentHash ident, std::vector<i2p::I2NPMessage *> msgs);
void PostCloseSession (std::shared_ptr<const i2p::data::RouterInfo> router);
void Connect (const boost::asio::ip::address& address, int port, std::shared_ptr<NTCPSession> conn);
void HandleConnect (const boost::system::error_code& ecode, std::shared_ptr<NTCPSession> conn);
bool ConnectToPeer (const i2p::data::IdentHash& ident, Peer& peer);
void HandlePeerCleanupTimer (const boost::system::error_code& ecode);
void NTCPResolve (const std::string& addr, const i2p::data::IdentHash& ident);
void HandleNTCPResolve (const boost::system::error_code& ecode, boost::asio::ip::tcp::resolver::iterator it,
i2p::data::IdentHash ident, std::shared_ptr<boost::asio::ip::tcp::resolver> resolver);
void DetectExternalIP ();
@@ -94,18 +111,20 @@ namespace transport
std::thread * m_Thread;
boost::asio::io_service m_Service;
boost::asio::io_service::work m_Work;
boost::asio::ip::tcp::acceptor * m_NTCPAcceptor, * m_NTCPV6Acceptor;
boost::asio::deadline_timer m_PeerCleanupTimer;
std::map<i2p::data::IdentHash, std::shared_ptr<NTCPSession> > m_NTCPSessions;
NTCPServer * m_NTCPServer;
SSUServer * m_SSUServer;
std::map<i2p::data::IdentHash, Peer> m_Peers;
DHKeysPairSupplier m_DHKeysPairSupplier;
public:
// for HTTP only
const decltype(m_NTCPSessions)& GetNTCPSessions () const { return m_NTCPSessions; };
const NTCPServer * GetNTCPServer () const { return m_NTCPServer; };
const SSUServer * GetSSUServer () const { return m_SSUServer; };
const decltype(m_Peers)& GetPeers () const { return m_Peers; };
};
extern Transports transports;

View File

@@ -27,7 +27,7 @@ namespace tunnel
delete m_Config;
}
void Tunnel::Build (uint32_t replyMsgID, OutboundTunnel * outboundTunnel)
void Tunnel::Build (uint32_t replyMsgID, std::shared_ptr<OutboundTunnel> outboundTunnel)
{
CryptoPP::RandomNumberGenerator& rnd = i2p::context.GetRandomNumberGenerator ();
auto numHops = m_Config->GetNumHops ();
@@ -150,11 +150,17 @@ namespace tunnel
hop = hop->prev;
}
}
void Tunnel::SendTunnelDataMsg (i2p::I2NPMessage * msg)
{
LogPrint (eLogInfo, "Can't send I2NP messages without delivery instructions");
DeleteI2NPMessage (msg);
}
void InboundTunnel::HandleTunnelDataMsg (I2NPMessage * msg)
{
if (IsFailed ()) SetState (eTunnelStateEstablished); // incoming messages means a tunnel is alive
msg->from = this;
msg->from = shared_from_this ();
EncryptTunnelMsg (msg);
m_Endpoint.HandleDecryptedTunnelDataMsg (msg);
}
@@ -189,36 +195,26 @@ namespace tunnel
m_Gateway.SendBuffer ();
}
void OutboundTunnel::HandleTunnelDataMsg (i2p::I2NPMessage * tunnelMsg)
{
LogPrint (eLogError, "Incoming message for outbound tunnel ", GetTunnelID ());
DeleteI2NPMessage (tunnelMsg);
}
Tunnels tunnels;
Tunnels::Tunnels (): m_IsRunning (false), m_Thread (nullptr), m_ExploratoryPool (nullptr)
Tunnels::Tunnels (): m_IsRunning (false), m_Thread (nullptr)
{
}
Tunnels::~Tunnels ()
{
for (auto& it : m_OutboundTunnels)
delete it;
m_OutboundTunnels.clear ();
for (auto& it : m_InboundTunnels)
delete it.second;
m_InboundTunnels.clear ();
for (auto& it : m_TransitTunnels)
delete it.second;
m_TransitTunnels.clear ();
/*for (auto& it : m_PendingTunnels)
delete it.second;
m_PendingTunnels.clear ();*/
for (auto& it: m_Pools)
delete it;
m_Pools.clear ();
}
InboundTunnel * Tunnels::GetInboundTunnel (uint32_t tunnelID)
std::shared_ptr<InboundTunnel> Tunnels::GetInboundTunnel (uint32_t tunnelID)
{
auto it = m_InboundTunnels.find(tunnelID);
if (it != m_InboundTunnels.end ())
@@ -233,11 +229,22 @@ namespace tunnel
return it->second;
return nullptr;
}
Tunnel * Tunnels::GetPendingTunnel (uint32_t replyMsgID)
std::shared_ptr<InboundTunnel> Tunnels::GetPendingInboundTunnel (uint32_t replyMsgID)
{
auto it = m_PendingTunnels.find(replyMsgID);
if (it != m_PendingTunnels.end () && it->second->GetState () == eTunnelStatePending)
return GetPendingTunnel (replyMsgID, m_PendingInboundTunnels);
}
std::shared_ptr<OutboundTunnel> Tunnels::GetPendingOutboundTunnel (uint32_t replyMsgID)
{
return GetPendingTunnel (replyMsgID, m_PendingOutboundTunnels);
}
template<class TTunnel>
std::shared_ptr<TTunnel> Tunnels::GetPendingTunnel (uint32_t replyMsgID, const std::map<uint32_t, std::shared_ptr<TTunnel> >& pendingTunnels)
{
auto it = pendingTunnels.find(replyMsgID);
if (it != pendingTunnels.end () && it->second->GetState () == eTunnelStatePending)
{
it->second->SetState (eTunnelStateBuildReplyReceived);
return it->second;
@@ -245,11 +252,10 @@ namespace tunnel
return nullptr;
}
InboundTunnel * Tunnels::GetNextInboundTunnel ()
std::shared_ptr<InboundTunnel> Tunnels::GetNextInboundTunnel ()
{
InboundTunnel * tunnel = nullptr;
std::shared_ptr<InboundTunnel> tunnel;
size_t minReceived = 0;
std::unique_lock<std::mutex> l(m_InboundTunnelsMutex);
for (auto it : m_InboundTunnels)
{
if (!it.second->IsEstablished ()) continue;
@@ -262,12 +268,11 @@ namespace tunnel
return tunnel;
}
OutboundTunnel * Tunnels::GetNextOutboundTunnel ()
std::shared_ptr<OutboundTunnel> Tunnels::GetNextOutboundTunnel ()
{
CryptoPP::RandomNumberGenerator& rnd = i2p::context.GetRandomNumberGenerator ();
uint32_t ind = rnd.GenerateWord32 (0, m_OutboundTunnels.size () - 1), i = 0;
OutboundTunnel * tunnel = nullptr;
std::unique_lock<std::mutex> l(m_OutboundTunnelsMutex);
std::shared_ptr<OutboundTunnel> tunnel;
for (auto it: m_OutboundTunnels)
{
if (it->IsEstablished ())
@@ -280,15 +285,15 @@ namespace tunnel
return tunnel;
}
TunnelPool * Tunnels::CreateTunnelPool (i2p::garlic::GarlicDestination * localDestination, int numInboundHops, int numOutboundHops)
std::shared_ptr<TunnelPool> Tunnels::CreateTunnelPool (i2p::garlic::GarlicDestination * localDestination, int numInboundHops, int numOutboundHops)
{
auto pool = new TunnelPool (localDestination, numInboundHops, numOutboundHops);
auto pool = std::make_shared<TunnelPool> (localDestination, numInboundHops, numOutboundHops);
std::unique_lock<std::mutex> l(m_PoolsMutex);
m_Pools.push_back (pool);
return pool;
}
void Tunnels::DeleteTunnelPool (TunnelPool * pool)
void Tunnels::DeleteTunnelPool (std::shared_ptr<TunnelPool> pool)
{
if (pool)
{
@@ -297,14 +302,10 @@ namespace tunnel
std::unique_lock<std::mutex> l(m_PoolsMutex);
m_Pools.remove (pool);
}
for (auto it: m_PendingTunnels)
if (it.second->GetTunnelPool () == pool)
it.second->SetTunnelPool (nullptr);
delete pool;
}
}
void Tunnels::StopTunnelPool (TunnelPool * pool)
void Tunnels::StopTunnelPool (std::shared_ptr<TunnelPool> pool)
{
if (pool)
{
@@ -316,7 +317,11 @@ namespace tunnel
void Tunnels::AddTransitTunnel (TransitTunnel * tunnel)
{
std::unique_lock<std::mutex> l(m_TransitTunnelsMutex);
m_TransitTunnels[tunnel->GetTunnelID ()] = tunnel;
if (!m_TransitTunnels.insert (std::make_pair (tunnel->GetTunnelID (), tunnel)).second)
{
LogPrint (eLogError, "Transit tunnel ", tunnel->GetTunnelID (), " already exists");
delete tunnel;
}
}
void Tunnels::Start ()
@@ -347,24 +352,69 @@ namespace tunnel
try
{
I2NPMessage * msg = m_Queue.GetNextWithTimeout (1000); // 1 sec
while (msg)
{
uint32_t tunnelID = bufbe32toh (msg->GetPayload ());
InboundTunnel * tunnel = GetInboundTunnel (tunnelID);
if (tunnel)
tunnel->HandleTunnelDataMsg (msg);
else
{
TransitTunnel * transitTunnel = GetTransitTunnel (tunnelID);
if (transitTunnel)
transitTunnel->HandleTunnelDataMsg (msg);
else
{
LogPrint ("Tunnel ", tunnelID, " not found");
i2p::DeleteI2NPMessage (msg);
}
}
msg = m_Queue.Get ();
if (msg)
{
uint32_t prevTunnelID = 0, tunnelID = 0;
TunnelBase * prevTunnel = nullptr;
do
{
TunnelBase * tunnel = nullptr;
uint8_t typeID = msg->GetTypeID ();
switch (typeID)
{
case eI2NPTunnelData:
case eI2NPTunnelGateway:
{
tunnelID = bufbe32toh (msg->GetPayload ());
if (tunnelID == prevTunnelID)
tunnel = prevTunnel;
else if (prevTunnel)
prevTunnel->FlushTunnelDataMsgs ();
if (!tunnel && typeID == eI2NPTunnelData)
tunnel = GetInboundTunnel (tunnelID).get ();
if (!tunnel)
tunnel = GetTransitTunnel (tunnelID);
if (tunnel)
{
if (typeID == eI2NPTunnelData)
tunnel->HandleTunnelDataMsg (msg);
else // tunnel gateway assumed
HandleTunnelGatewayMsg (tunnel, msg);
}
else
{
LogPrint (eLogWarning, "Tunnel ", tunnelID, " not found");
DeleteI2NPMessage (msg);
}
break;
}
case eI2NPVariableTunnelBuild:
case eI2NPVariableTunnelBuildReply:
case eI2NPTunnelBuild:
case eI2NPTunnelBuildReply:
{
HandleI2NPMessage (msg->GetBuffer (), msg->GetLength ());
DeleteI2NPMessage (msg);
break;
}
default:
{
LogPrint (eLogError, "Unexpected messsage type ", (int)typeID);
DeleteI2NPMessage (msg);
}
}
msg = m_Queue.Get ();
if (msg)
{
prevTunnelID = tunnelID;
prevTunnel = tunnel;
}
else if (tunnel)
tunnel->FlushTunnelDataMsgs ();
}
while (msg);
}
uint64_t ts = i2p::util::GetSecondsSinceEpoch ();
@@ -381,6 +431,33 @@ namespace tunnel
}
}
void Tunnels::HandleTunnelGatewayMsg (TunnelBase * tunnel, I2NPMessage * msg)
{
if (!tunnel)
{
LogPrint (eLogError, "Missing tunnel for TunnelGateway");
i2p::DeleteI2NPMessage (msg);
return;
}
const uint8_t * payload = msg->GetPayload ();
uint16_t len = bufbe16toh(payload + TUNNEL_GATEWAY_HEADER_LENGTH_OFFSET);
// we make payload as new I2NP message to send
msg->offset += I2NP_HEADER_SIZE + TUNNEL_GATEWAY_HEADER_SIZE;
msg->len = msg->offset + len;
auto typeID = msg->GetTypeID ();
LogPrint (eLogDebug, "TunnelGateway of ", (int)len, " bytes for tunnel ", tunnel->GetTunnelID (), ". Msg type ", (int)typeID);
if (typeID == eI2NPDatabaseStore || typeID == eI2NPDatabaseSearchReply)
{
// transit DatabaseStore my contain new/updated RI
// or DatabaseSearchReply with new routers
auto ds = NewI2NPMessage ();
*ds = *msg;
i2p::data::netdb.PostI2NPMsg (ds);
}
tunnel->SendTunnelDataMsg (msg);
}
void Tunnels::ManageTunnels ()
{
ManagePendingTunnels ();
@@ -391,10 +468,17 @@ namespace tunnel
}
void Tunnels::ManagePendingTunnels ()
{
ManagePendingTunnels (m_PendingInboundTunnels);
ManagePendingTunnels (m_PendingOutboundTunnels);
}
template<class PendingTunnels>
void Tunnels::ManagePendingTunnels (PendingTunnels& pendingTunnels)
{
// check pending tunnel. delete failed or timeout
uint64_t ts = i2p::util::GetSecondsSinceEpoch ();
for (auto it = m_PendingTunnels.begin (); it != m_PendingTunnels.end ();)
for (auto it = pendingTunnels.begin (); it != pendingTunnels.end ();)
{
auto tunnel = it->second;
switch (tunnel->GetState ())
@@ -403,23 +487,21 @@ namespace tunnel
if (ts > tunnel->GetCreationTime () + TUNNEL_CREATION_TIMEOUT)
{
LogPrint ("Pending tunnel build request ", it->first, " timeout. Deleted");
delete tunnel;
it = m_PendingTunnels.erase (it);
it = pendingTunnels.erase (it);
}
else
it++;
break;
case eTunnelStateBuildFailed:
LogPrint ("Pending tunnel build request ", it->first, " failed. Deleted");
delete tunnel;
it = m_PendingTunnels.erase (it);
it = pendingTunnels.erase (it);
break;
case eTunnelStateBuildReplyReceived:
// intermidiate state, will be either established of build failed
// intermediate state, will be either established of build failed
it++;
break;
default:
it = m_PendingTunnels.erase (it);
it = pendingTunnels.erase (it);
}
}
}
@@ -435,16 +517,11 @@ namespace tunnel
{
LogPrint ("Tunnel ", tunnel->GetTunnelID (), " expired");
{
std::unique_lock<std::mutex> l(m_PoolsMutex);
auto pool = tunnel->GetTunnelPool ();
if (pool)
pool->TunnelExpired (tunnel);
}
{
std::unique_lock<std::mutex> l(m_OutboundTunnelsMutex);
it = m_OutboundTunnels.erase (it);
}
delete tunnel;
it = m_OutboundTunnels.erase (it);
}
else
{
@@ -458,7 +535,7 @@ namespace tunnel
if (m_OutboundTunnels.size () < 5)
{
// trying to create one more oubound tunnel
InboundTunnel * inboundTunnel = GetNextInboundTunnel ();
auto inboundTunnel = GetNextInboundTunnel ();
if (!inboundTunnel) return;
LogPrint ("Creating one hop outbound tunnel...");
CreateTunnel<OutboundTunnel> (
@@ -481,16 +558,11 @@ namespace tunnel
{
LogPrint ("Tunnel ", tunnel->GetTunnelID (), " expired");
{
std::unique_lock<std::mutex> l(m_PoolsMutex);
auto pool = tunnel->GetTunnelPool ();
if (pool)
pool->TunnelExpired (tunnel);
}
{
std::unique_lock<std::mutex> l(m_InboundTunnelsMutex);
it = m_InboundTunnels.erase (it);
}
delete tunnel;
it = m_InboundTunnels.erase (it);
}
else
{
@@ -529,8 +601,8 @@ namespace tunnel
{
if (ts > it->second->GetCreationTime () + TUNNEL_EXPIRATION_TIMEOUT)
{
LogPrint ("Transit tunnel ", it->second->GetTunnelID (), " expired");
auto tmp = it->second;
LogPrint ("Transit tunnel ", tmp->GetTunnelID (), " expired");
{
std::unique_lock<std::mutex> l(m_TransitTunnelsMutex);
it = m_TransitTunnels.erase (it);
@@ -547,8 +619,8 @@ namespace tunnel
std::unique_lock<std::mutex> l(m_PoolsMutex);
for (auto it: m_Pools)
{
TunnelPool * pool = it;
if (pool->IsActive ())
auto pool = it;
if (pool && pool->IsActive ())
{
pool->CreateTunnels ();
pool->TestTunnels ();
@@ -561,19 +633,33 @@ namespace tunnel
if (msg) m_Queue.Put (msg);
}
template<class TTunnel>
TTunnel * Tunnels::CreateTunnel (TunnelConfig * config, OutboundTunnel * outboundTunnel)
void Tunnels::PostTunnelData (const std::vector<I2NPMessage *>& msgs)
{
TTunnel * newTunnel = new TTunnel (config);
m_Queue.Put (msgs);
}
template<class TTunnel>
std::shared_ptr<TTunnel> Tunnels::CreateTunnel (TunnelConfig * config, std::shared_ptr<OutboundTunnel> outboundTunnel)
{
auto newTunnel = std::make_shared<TTunnel> (config);
uint32_t replyMsgID = i2p::context.GetRandomNumberGenerator ().GenerateWord32 ();
m_PendingTunnels[replyMsgID] = newTunnel;
AddPendingTunnel (replyMsgID, newTunnel);
newTunnel->Build (replyMsgID, outboundTunnel);
return newTunnel;
}
void Tunnels::AddOutboundTunnel (OutboundTunnel * newTunnel)
void Tunnels::AddPendingTunnel (uint32_t replyMsgID, std::shared_ptr<InboundTunnel> tunnel)
{
m_PendingInboundTunnels[replyMsgID] = tunnel;
}
void Tunnels::AddPendingTunnel (uint32_t replyMsgID, std::shared_ptr<OutboundTunnel> tunnel)
{
m_PendingOutboundTunnels[replyMsgID] = tunnel;
}
void Tunnels::AddOutboundTunnel (std::shared_ptr<OutboundTunnel> newTunnel)
{
std::unique_lock<std::mutex> l(m_OutboundTunnelsMutex);
m_OutboundTunnels.push_back (newTunnel);
auto pool = newTunnel->GetTunnelPool ();
if (pool && pool->IsActive ())
@@ -582,9 +668,8 @@ namespace tunnel
newTunnel->SetTunnelPool (nullptr);
}
void Tunnels::AddInboundTunnel (InboundTunnel * newTunnel)
void Tunnels::AddInboundTunnel (std::shared_ptr<InboundTunnel> newTunnel)
{
std::unique_lock<std::mutex> l(m_InboundTunnelsMutex);
m_InboundTunnels[newTunnel->GetTunnelID ()] = newTunnel;
auto pool = newTunnel->GetTunnelPool ();
if (!pool)
@@ -610,5 +695,18 @@ namespace tunnel
i2p::context.GetSharedRouterInfo ()
}));
}
int Tunnels::GetTransitTunnelsExpirationTimeout ()
{
int timeout = 0;
uint32_t ts = i2p::util::GetSecondsSinceEpoch ();
std::unique_lock<std::mutex> l(m_TransitTunnelsMutex);
for (auto it: m_TransitTunnels)
{
int t = it.second->GetCreationTime () + TUNNEL_EXPIRATION_TIMEOUT - ts;
if (t > timeout) timeout = t;
}
return timeout;
}
}
}

View File

@@ -8,6 +8,7 @@
#include <string>
#include <thread>
#include <mutex>
#include <memory>
#include "Queue.h"
#include "TunnelConfig.h"
#include "TunnelPool.h"
@@ -46,7 +47,7 @@ namespace tunnel
Tunnel (TunnelConfig * config);
~Tunnel ();
void Build (uint32_t replyMsgID, OutboundTunnel * outboundTunnel = 0);
void Build (uint32_t replyMsgID, std::shared_ptr<OutboundTunnel> outboundTunnel = nullptr);
TunnelConfig * GetTunnelConfig () const { return m_Config; }
TunnelState GetState () const { return m_State; };
@@ -54,12 +55,13 @@ namespace tunnel
bool IsEstablished () const { return m_State == eTunnelStateEstablished; };
bool IsFailed () const { return m_State == eTunnelStateFailed; };
TunnelPool * GetTunnelPool () const { return m_Pool; };
void SetTunnelPool (TunnelPool * pool) { m_Pool = pool; };
std::shared_ptr<TunnelPool> GetTunnelPool () const { return m_Pool; };
void SetTunnelPool (std::shared_ptr<TunnelPool> pool) { m_Pool = pool; };
bool HandleTunnelBuildResponse (uint8_t * msg, size_t len);
// implements TunnelBase
void SendTunnelDataMsg (i2p::I2NPMessage * msg);
void EncryptTunnelMsg (I2NPMessage * tunnelMsg);
uint32_t GetNextTunnelID () const { return m_Config->GetFirstHop ()->tunnelID; };
const i2p::data::IdentHash& GetNextIdentHash () const { return m_Config->GetFirstHop ()->router->GetIdentHash (); };
@@ -67,7 +69,7 @@ namespace tunnel
private:
TunnelConfig * m_Config;
TunnelPool * m_Pool; // pool, tunnel belongs to, or null
std::shared_ptr<TunnelPool> m_Pool; // pool, tunnel belongs to, or null
TunnelState m_State;
};
@@ -84,6 +86,7 @@ namespace tunnel
size_t GetNumSentBytes () const { return m_Gateway.GetNumSentBytes (); };
// implements TunnelBase
void HandleTunnelDataMsg (i2p::I2NPMessage * tunnelMsg);
uint32_t GetTunnelID () const { return GetNextTunnelID (); };
private:
@@ -92,7 +95,7 @@ namespace tunnel
TunnelGateway m_Gateway;
};
class InboundTunnel: public Tunnel
class InboundTunnel: public Tunnel, public std::enable_shared_from_this<InboundTunnel>
{
public:
@@ -117,30 +120,42 @@ namespace tunnel
void Start ();
void Stop ();
InboundTunnel * GetInboundTunnel (uint32_t tunnelID);
Tunnel * GetPendingTunnel (uint32_t replyMsgID);
InboundTunnel * GetNextInboundTunnel ();
OutboundTunnel * GetNextOutboundTunnel ();
TunnelPool * GetExploratoryPool () const { return m_ExploratoryPool; };
std::shared_ptr<InboundTunnel> GetInboundTunnel (uint32_t tunnelID);
std::shared_ptr<InboundTunnel> GetPendingInboundTunnel (uint32_t replyMsgID);
std::shared_ptr<OutboundTunnel> GetPendingOutboundTunnel (uint32_t replyMsgID);
std::shared_ptr<InboundTunnel> GetNextInboundTunnel ();
std::shared_ptr<OutboundTunnel> GetNextOutboundTunnel ();
std::shared_ptr<TunnelPool> GetExploratoryPool () const { return m_ExploratoryPool; };
TransitTunnel * GetTransitTunnel (uint32_t tunnelID);
int GetTransitTunnelsExpirationTimeout ();
void AddTransitTunnel (TransitTunnel * tunnel);
void AddOutboundTunnel (OutboundTunnel * newTunnel);
void AddInboundTunnel (InboundTunnel * newTunnel);
void AddOutboundTunnel (std::shared_ptr<OutboundTunnel> newTunnel);
void AddInboundTunnel (std::shared_ptr<InboundTunnel> newTunnel);
void PostTunnelData (I2NPMessage * msg);
void PostTunnelData (const std::vector<I2NPMessage *>& msgs);
template<class TTunnel>
TTunnel * CreateTunnel (TunnelConfig * config, OutboundTunnel * outboundTunnel = 0);
TunnelPool * CreateTunnelPool (i2p::garlic::GarlicDestination * localDestination, int numInboundHops, int numOuboundHops);
void DeleteTunnelPool (TunnelPool * pool);
void StopTunnelPool (TunnelPool * pool);
std::shared_ptr<TTunnel> CreateTunnel (TunnelConfig * config, std::shared_ptr<OutboundTunnel> outboundTunnel = nullptr);
void AddPendingTunnel (uint32_t replyMsgID, std::shared_ptr<InboundTunnel> tunnel);
void AddPendingTunnel (uint32_t replyMsgID, std::shared_ptr<OutboundTunnel> tunnel);
std::shared_ptr<TunnelPool> CreateTunnelPool (i2p::garlic::GarlicDestination * localDestination, int numInboundHops, int numOuboundHops);
void DeleteTunnelPool (std::shared_ptr<TunnelPool> pool);
void StopTunnelPool (std::shared_ptr<TunnelPool> pool);
private:
template<class TTunnel>
std::shared_ptr<TTunnel> GetPendingTunnel (uint32_t replyMsgID, const std::map<uint32_t, std::shared_ptr<TTunnel> >& pendingTunnels);
void HandleTunnelGatewayMsg (TunnelBase * tunnel, I2NPMessage * msg);
void Run ();
void ManageTunnels ();
void ManageOutboundTunnels ();
void ManageInboundTunnels ();
void ManageTransitTunnels ();
void ManagePendingTunnels ();
template<class PendingTunnels>
void ManagePendingTunnels (PendingTunnels& pendingTunnels);
void ManageTunnelPools ();
void CreateZeroHopsInboundTunnel ();
@@ -149,16 +164,15 @@ namespace tunnel
bool m_IsRunning;
std::thread * m_Thread;
std::map<uint32_t, Tunnel *> m_PendingTunnels; // by replyMsgID
std::mutex m_InboundTunnelsMutex;
std::map<uint32_t, InboundTunnel *> m_InboundTunnels;
std::mutex m_OutboundTunnelsMutex;
std::list<OutboundTunnel *> m_OutboundTunnels;
std::map<uint32_t, std::shared_ptr<InboundTunnel> > m_PendingInboundTunnels; // by replyMsgID
std::map<uint32_t, std::shared_ptr<OutboundTunnel> > m_PendingOutboundTunnels; // by replyMsgID
std::map<uint32_t, std::shared_ptr<InboundTunnel> > m_InboundTunnels;
std::list<std::shared_ptr<OutboundTunnel> > m_OutboundTunnels;
std::mutex m_TransitTunnelsMutex;
std::map<uint32_t, TransitTunnel *> m_TransitTunnels;
std::mutex m_PoolsMutex;
std::list<TunnelPool *> m_Pools;
TunnelPool * m_ExploratoryPool;
std::list<std::shared_ptr<TunnelPool>> m_Pools;
std::shared_ptr<TunnelPool> m_ExploratoryPool;
i2p::util::Queue<I2NPMessage> m_Queue;
public:
@@ -167,6 +181,7 @@ namespace tunnel
const decltype(m_OutboundTunnels)& GetOutboundTunnels () const { return m_OutboundTunnels; };
const decltype(m_InboundTunnels)& GetInboundTunnels () const { return m_InboundTunnels; };
const decltype(m_TransitTunnels)& GetTransitTunnels () const { return m_TransitTunnels; };
int GetQueueSize () { return m_Queue.GetSize (); };
};
extern Tunnels tunnels;

View File

@@ -2,6 +2,7 @@
#define TUNNEL_BASE_H__
#include <inttypes.h>
#include <memory>
#include "Timestamp.h"
#include "I2NPProtocol.h"
#include "Identity.h"
@@ -36,6 +37,9 @@ namespace tunnel
TunnelBase (): m_CreationTime (i2p::util::GetSecondsSinceEpoch ()) {};
virtual ~TunnelBase () {};
virtual void HandleTunnelDataMsg (i2p::I2NPMessage * tunnelMsg) = 0;
virtual void SendTunnelDataMsg (i2p::I2NPMessage * msg) = 0;
virtual void FlushTunnelDataMsgs () {};
virtual void EncryptTunnelMsg (I2NPMessage * tunnelMsg) = 0;
virtual uint32_t GetNextTunnelID () const = 0;
virtual const i2p::data::IdentHash& GetNextIdentHash () const = 0;
@@ -51,7 +55,7 @@ namespace tunnel
struct TunnelCreationTimeCmp
{
bool operator() (const TunnelBase * t1, const TunnelBase * t2) const
bool operator() (std::shared_ptr<const TunnelBase> t1, std::shared_ptr<const TunnelBase> t2) const
{
if (t1->GetCreationTime () != t2->GetCreationTime ())
return t1->GetCreationTime () > t2->GetCreationTime ();

View File

@@ -27,7 +27,6 @@ namespace tunnel
uint8_t * zero = (uint8_t *)memchr (decrypted + 4, 0, TUNNEL_DATA_ENCRYPTED_SIZE - 4); // witout 4-byte checksum
if (zero)
{
LogPrint ("TunnelMessage: zero found at ", (int)(zero-decrypted));
uint8_t * fragment = zero + 1;
// verify checksum
memcpy (msg->GetPayload () + TUNNEL_DATA_MSG_SIZE, msg->GetPayload () + 4, 16); // copy iv to the end
@@ -35,7 +34,7 @@ namespace tunnel
CryptoPP::SHA256().CalculateDigest (hash, fragment, TUNNEL_DATA_MSG_SIZE -(fragment - msg->GetPayload ()) + 16); // payload + iv
if (memcmp (hash, decrypted, 4))
{
LogPrint ("TunnelMessage: checksum verification failed");
LogPrint (eLogError, "TunnelMessage: checksum verification failed");
i2p::DeleteI2NPMessage (msg);
return;
}
@@ -57,17 +56,14 @@ namespace tunnel
switch (m.deliveryType)
{
case eDeliveryTypeLocal: // 0
LogPrint ("Delivery type local");
break;
case eDeliveryTypeTunnel: // 1
LogPrint ("Delivery type tunnel");
m.tunnelID = bufbe32toh (fragment);
fragment += 4; // tunnelID
m.hash = i2p::data::IdentHash (fragment);
fragment += 32; // hash
break;
case eDeliveryTypeRouter: // 2
LogPrint ("Delivery type router");
m.hash = i2p::data::IdentHash (fragment);
fragment += 32; // to hash
break;
@@ -81,7 +77,6 @@ namespace tunnel
// Message ID
msgID = bufbe32toh (fragment);
fragment += 4;
LogPrint ("Fragmented message ", msgID);
isLastFragment = false;
}
}
@@ -92,12 +87,10 @@ namespace tunnel
fragment += 4;
fragmentNum = (flag >> 1) & 0x3F; // 6 bits
isLastFragment = flag & 0x01;
LogPrint ("Follow on fragment ", fragmentNum, " of message ", msgID, isLastFragment ? " last" : " non-last");
}
uint16_t size = bufbe16toh (fragment);
fragment += 2;
LogPrint ("Fragment size=", (int)size);
msg->offset = fragment - msg->buf;
msg->len = msg->offset + size;
@@ -121,9 +114,14 @@ namespace tunnel
if (!isFollowOnFragment) // create new incomlete message
{
m.nextFragmentNum = 1;
auto& msg = m_IncompleteMessages[msgID];
msg = m;
HandleOutOfSequenceFragment (msgID, msg);
auto ret = m_IncompleteMessages.insert (std::pair<uint32_t, TunnelMessageBlockEx>(msgID, m));
if (ret.second)
HandleOutOfSequenceFragment (msgID, ret.first->second);
else
{
LogPrint (eLogError, "Incomplete message ", msgID, "already exists");
DeleteI2NPMessage (m.data);
}
}
else
{
@@ -131,8 +129,11 @@ namespace tunnel
HandleFollowOnFragment (msgID, isLastFragment, m);
}
}
else
LogPrint ("Message is fragmented, but msgID is not presented");
else
{
LogPrint (eLogError, "Message is fragmented, but msgID is not presented");
DeleteI2NPMessage (m.data);
}
}
fragment += size;
@@ -140,7 +141,7 @@ namespace tunnel
}
else
{
LogPrint ("TunnelMessage: zero not found");
LogPrint (eLogError, "TunnelMessage: zero not found");
i2p::DeleteI2NPMessage (msg);
}
}
@@ -173,7 +174,7 @@ namespace tunnel
}
else
{
LogPrint ("Fragment ", m.nextFragmentNum, " of message ", msgID, "exceeds max I2NP message size. Message dropped");
LogPrint (eLogError, "Fragment ", m.nextFragmentNum, " of message ", msgID, "exceeds max I2NP message size. Message dropped");
i2p::DeleteI2NPMessage (msg.data);
m_IncompleteMessages.erase (it);
}
@@ -181,13 +182,13 @@ namespace tunnel
}
else
{
LogPrint ("Unexpected fragment ", (int)m.nextFragmentNum, " instead ", (int)msg.nextFragmentNum, " of message ", msgID, ". Saved");
LogPrint (eLogInfo, "Unexpected fragment ", (int)m.nextFragmentNum, " instead ", (int)msg.nextFragmentNum, " of message ", msgID, ". Saved");
AddOutOfSequenceFragment (msgID, m.nextFragmentNum, isLastFragment, m.data);
}
}
else
{
LogPrint ("First fragment of message ", msgID, " not found. Saved");
LogPrint (eLogInfo, "First fragment of message ", msgID, " not found. Saved");
AddOutOfSequenceFragment (msgID, m.nextFragmentNum, isLastFragment, m.data);
}
}
@@ -208,7 +209,7 @@ namespace tunnel
{
if (it->second.fragmentNum == msg.nextFragmentNum)
{
LogPrint ("Out-of-sequence fragment ", (int)it->second.fragmentNum, " of message ", msgID, " found");
LogPrint (eLogInfo, "Out-of-sequence fragment ", (int)it->second.fragmentNum, " of message ", msgID, " found");
auto size = it->second.data->GetLength ();
memcpy (msg.data->buf + msg.data->len, it->second.data->GetBuffer (), size); // concatenate out-of-sync fragment
msg.data->len += size;
@@ -228,7 +229,7 @@ namespace tunnel
void TunnelEndpoint::HandleNextMessage (const TunnelMessageBlock& msg)
{
LogPrint ("TunnelMessage: handle fragment of ", msg.data->GetLength ()," bytes. Msg type ", (int)msg.data->GetTypeID ());
LogPrint (eLogInfo, "TunnelMessage: handle fragment of ", msg.data->GetLength ()," bytes. Msg type ", (int)msg.data->GetTypeID ());
switch (msg.deliveryType)
{
case eDeliveryTypeLocal:
@@ -257,13 +258,16 @@ namespace tunnel
}
else // we shouldn't send this message. possible leakage
{
LogPrint ("Message to another router arrived from an inbound tunnel. Dropped");
LogPrint (eLogError, "Message to another router arrived from an inbound tunnel. Dropped");
i2p::DeleteI2NPMessage (msg.data);
}
}
break;
default:
LogPrint ("TunnelMessage: Unknown delivery type ", (int)msg.deliveryType);
{
LogPrint (eLogError, "TunnelMessage: Unknown delivery type ", (int)msg.deliveryType);
i2p::DeleteI2NPMessage (msg.data);
}
};
}
}

View File

@@ -10,6 +10,12 @@ namespace i2p
{
namespace tunnel
{
TunnelGatewayBuffer::~TunnelGatewayBuffer ()
{
for (auto it: m_TunnelDataMsgs)
DeleteI2NPMessage (it);
}
void TunnelGatewayBuffer::PutI2NPMsg (const TunnelMessageBlock& block)
{
bool messageCreated = false;
@@ -188,9 +194,9 @@ namespace tunnel
{
m_Tunnel->EncryptTunnelMsg (tunnelMsg);
FillI2NPMessageHeader (tunnelMsg, eI2NPTunnelData);
i2p::transport::transports.SendMessage (m_Tunnel->GetNextIdentHash (), tunnelMsg);
m_NumSentBytes += TUNNEL_DATA_MSG_SIZE;
}
i2p::transport::transports.SendMessages (m_Tunnel->GetNextIdentHash (), tunnelMsgs);
m_Buffer.ClearTunnelDataMsgs ();
}
}

View File

@@ -15,6 +15,7 @@ namespace tunnel
public:
TunnelGatewayBuffer (uint32_t tunnelID): m_TunnelID (tunnelID),
m_CurrentTunnelDataMsg (nullptr), m_RemainingSize (0) {};
~TunnelGatewayBuffer ();
void PutI2NPMsg (const TunnelMessageBlock& block);
const std::vector<I2NPMessage *>& GetTunnelDataMsgs () const { return m_TunnelDataMsgs; };
void ClearTunnelDataMsgs ();

View File

@@ -38,7 +38,7 @@ namespace tunnel
m_Tests.clear ();
}
void TunnelPool::TunnelCreated (InboundTunnel * createdTunnel)
void TunnelPool::TunnelCreated (std::shared_ptr<InboundTunnel> createdTunnel)
{
if (!m_IsActive) return;
{
@@ -49,7 +49,7 @@ namespace tunnel
m_LocalDestination->SetLeaseSetUpdated ();
}
void TunnelPool::TunnelExpired (InboundTunnel * expiredTunnel)
void TunnelPool::TunnelExpired (std::shared_ptr<InboundTunnel> expiredTunnel)
{
if (expiredTunnel)
{
@@ -63,14 +63,14 @@ namespace tunnel
}
}
void TunnelPool::TunnelCreated (OutboundTunnel * createdTunnel)
void TunnelPool::TunnelCreated (std::shared_ptr<OutboundTunnel> createdTunnel)
{
if (!m_IsActive) return;
std::unique_lock<std::mutex> l(m_OutboundTunnelsMutex);
m_OutboundTunnels.insert (createdTunnel);
}
void TunnelPool::TunnelExpired (OutboundTunnel * expiredTunnel)
void TunnelPool::TunnelExpired (std::shared_ptr<OutboundTunnel> expiredTunnel)
{
if (expiredTunnel)
{
@@ -84,9 +84,9 @@ namespace tunnel
}
}
std::vector<InboundTunnel *> TunnelPool::GetInboundTunnels (int num) const
std::vector<std::shared_ptr<InboundTunnel> > TunnelPool::GetInboundTunnels (int num) const
{
std::vector<InboundTunnel *> v;
std::vector<std::shared_ptr<InboundTunnel> > v;
int i = 0;
std::unique_lock<std::mutex> l(m_InboundTunnelsMutex);
for (auto it : m_InboundTunnels)
@@ -101,26 +101,22 @@ namespace tunnel
return v;
}
OutboundTunnel * TunnelPool::GetNextOutboundTunnel (OutboundTunnel * suggested) const
std::shared_ptr<OutboundTunnel> TunnelPool::GetNextOutboundTunnel () const
{
std::unique_lock<std::mutex> l(m_OutboundTunnelsMutex);
return GetNextTunnel (m_OutboundTunnels, suggested);
return GetNextTunnel (m_OutboundTunnels);
}
InboundTunnel * TunnelPool::GetNextInboundTunnel (InboundTunnel * suggested) const
std::shared_ptr<InboundTunnel> TunnelPool::GetNextInboundTunnel () const
{
std::unique_lock<std::mutex> l(m_InboundTunnelsMutex);
return GetNextTunnel (m_InboundTunnels, suggested);
return GetNextTunnel (m_InboundTunnels);
}
template<class TTunnels>
typename TTunnels::value_type TunnelPool::GetNextTunnel (TTunnels& tunnels,
typename TTunnels::value_type suggested) const
typename TTunnels::value_type TunnelPool::GetNextTunnel (TTunnels& tunnels) const
{
if (tunnels.empty ()) return nullptr;
if (suggested && tunnels.count (suggested) > 0 && suggested->IsEstablished ())
return suggested;
if (tunnels.empty ()) return nullptr;
CryptoPP::RandomNumberGenerator& rnd = i2p::context.GetRandomNumberGenerator ();
uint32_t ind = rnd.GenerateWord32 (0, tunnels.size ()/2), i = 0;
typename TTunnels::value_type tunnel = nullptr;
@@ -274,7 +270,7 @@ namespace tunnel
void TunnelPool::CreateInboundTunnel ()
{
OutboundTunnel * outboundTunnel = GetNextOutboundTunnel ();
auto outboundTunnel = GetNextOutboundTunnel ();
if (!outboundTunnel)
outboundTunnel = tunnels.GetNextOutboundTunnel ();
LogPrint ("Creating destination inbound tunnel...");
@@ -299,23 +295,23 @@ namespace tunnel
hops.push_back (hop);
}
std::reverse (hops.begin (), hops.end ());
auto * tunnel = tunnels.CreateTunnel<InboundTunnel> (new TunnelConfig (hops), outboundTunnel);
tunnel->SetTunnelPool (this);
auto tunnel = tunnels.CreateTunnel<InboundTunnel> (new TunnelConfig (hops), outboundTunnel);
tunnel->SetTunnelPool (shared_from_this ());
}
void TunnelPool::RecreateInboundTunnel (InboundTunnel * tunnel)
void TunnelPool::RecreateInboundTunnel (std::shared_ptr<InboundTunnel> tunnel)
{
OutboundTunnel * outboundTunnel = GetNextOutboundTunnel ();
auto outboundTunnel = GetNextOutboundTunnel ();
if (!outboundTunnel)
outboundTunnel = tunnels.GetNextOutboundTunnel ();
LogPrint ("Re-creating destination inbound tunnel...");
auto * newTunnel = tunnels.CreateTunnel<InboundTunnel> (tunnel->GetTunnelConfig ()->Clone (), outboundTunnel);
newTunnel->SetTunnelPool (this);
auto newTunnel = tunnels.CreateTunnel<InboundTunnel> (tunnel->GetTunnelConfig ()->Clone (), outboundTunnel);
newTunnel->SetTunnelPool (shared_from_this());
}
void TunnelPool::CreateOutboundTunnel ()
{
InboundTunnel * inboundTunnel = GetNextInboundTunnel ();
auto inboundTunnel = GetNextInboundTunnel ();
if (!inboundTunnel)
inboundTunnel = tunnels.GetNextInboundTunnel ();
if (inboundTunnel)
@@ -331,25 +327,25 @@ namespace tunnel
hops.push_back (hop);
}
auto * tunnel = tunnels.CreateTunnel<OutboundTunnel> (
auto tunnel = tunnels.CreateTunnel<OutboundTunnel> (
new TunnelConfig (hops, inboundTunnel->GetTunnelConfig ()));
tunnel->SetTunnelPool (this);
tunnel->SetTunnelPool (shared_from_this ());
}
else
LogPrint ("Can't create outbound tunnel. No inbound tunnels found");
}
void TunnelPool::RecreateOutboundTunnel (OutboundTunnel * tunnel)
void TunnelPool::RecreateOutboundTunnel (std::shared_ptr<OutboundTunnel> tunnel)
{
InboundTunnel * inboundTunnel = GetNextInboundTunnel ();
auto inboundTunnel = GetNextInboundTunnel ();
if (!inboundTunnel)
inboundTunnel = tunnels.GetNextInboundTunnel ();
if (inboundTunnel)
{
LogPrint ("Re-creating destination outbound tunnel...");
auto * newTunnel = tunnels.CreateTunnel<OutboundTunnel> (
auto newTunnel = tunnels.CreateTunnel<OutboundTunnel> (
tunnel->GetTunnelConfig ()->Clone (inboundTunnel->GetTunnelConfig ()));
newTunnel->SetTunnelPool (this);
newTunnel->SetTunnelPool (shared_from_this ());
}
else
LogPrint ("Can't re-create outbound tunnel. No inbound tunnels found");

View File

@@ -6,6 +6,7 @@
#include <vector>
#include <utility>
#include <mutex>
#include <memory>
#include "Identity.h"
#include "LeaseSet.h"
#include "RouterInfo.h"
@@ -22,7 +23,7 @@ namespace tunnel
class InboundTunnel;
class OutboundTunnel;
class TunnelPool // per local destination
class TunnelPool: public std::enable_shared_from_this<TunnelPool> // per local destination
{
public:
@@ -33,13 +34,13 @@ namespace tunnel
void SetLocalDestination (i2p::garlic::GarlicDestination * destination) { m_LocalDestination = destination; };
void CreateTunnels ();
void TunnelCreated (InboundTunnel * createdTunnel);
void TunnelExpired (InboundTunnel * expiredTunnel);
void TunnelCreated (OutboundTunnel * createdTunnel);
void TunnelExpired (OutboundTunnel * expiredTunnel);
std::vector<InboundTunnel *> GetInboundTunnels (int num) const;
OutboundTunnel * GetNextOutboundTunnel (OutboundTunnel * suggested = nullptr) const;
InboundTunnel * GetNextInboundTunnel (InboundTunnel * suggested = nullptr) const;
void TunnelCreated (std::shared_ptr<InboundTunnel> createdTunnel);
void TunnelExpired (std::shared_ptr<InboundTunnel> expiredTunnel);
void TunnelCreated (std::shared_ptr<OutboundTunnel> createdTunnel);
void TunnelExpired (std::shared_ptr<OutboundTunnel> expiredTunnel);
std::vector<std::shared_ptr<InboundTunnel> > GetInboundTunnels (int num) const;
std::shared_ptr<OutboundTunnel> GetNextOutboundTunnel () const;
std::shared_ptr<InboundTunnel> GetNextInboundTunnel () const;
void TestTunnels ();
void ProcessGarlicMessage (I2NPMessage * msg);
@@ -53,11 +54,10 @@ namespace tunnel
void CreateInboundTunnel ();
void CreateOutboundTunnel ();
void RecreateInboundTunnel (InboundTunnel * tunnel);
void RecreateOutboundTunnel (OutboundTunnel * tunnel);
void RecreateInboundTunnel (std::shared_ptr<InboundTunnel> tunnel);
void RecreateOutboundTunnel (std::shared_ptr<OutboundTunnel> tunnel);
template<class TTunnels>
typename TTunnels::value_type GetNextTunnel (TTunnels& tunnels,
typename TTunnels::value_type suggested = nullptr) const;
typename TTunnels::value_type GetNextTunnel (TTunnels& tunnels) const;
std::shared_ptr<const i2p::data::RouterInfo> SelectNextHop (std::shared_ptr<const i2p::data::RouterInfo> prevHop) const;
private:
@@ -65,10 +65,10 @@ namespace tunnel
i2p::garlic::GarlicDestination * m_LocalDestination;
int m_NumInboundHops, m_NumOutboundHops, m_NumTunnels;
mutable std::mutex m_InboundTunnelsMutex;
std::set<InboundTunnel *, TunnelCreationTimeCmp> m_InboundTunnels; // recent tunnel appears first
std::set<std::shared_ptr<InboundTunnel>, TunnelCreationTimeCmp> m_InboundTunnels; // recent tunnel appears first
mutable std::mutex m_OutboundTunnelsMutex;
std::set<OutboundTunnel *, TunnelCreationTimeCmp> m_OutboundTunnels;
std::map<uint32_t, std::pair<OutboundTunnel *, InboundTunnel *> > m_Tests;
std::set<std::shared_ptr<OutboundTunnel>, TunnelCreationTimeCmp> m_OutboundTunnels;
std::map<uint32_t, std::pair<std::shared_ptr<OutboundTunnel>, std::shared_ptr<InboundTunnel> > > m_Tests;
bool m_IsActive;
public:

View File

@@ -22,7 +22,7 @@
<ClCompile Include="..\AddressBook.cpp" />
<ClCompile Include="..\aes.cpp" />
<ClCompile Include="..\base64.cpp" />
<ClCompile Include="..\BOB.cpp" />
<ClCompile Include="..\BOB.cpp" />
<ClCompile Include="..\CryptoConst.cpp" />
<ClCompile Include="..\Daemon.cpp" />
<ClCompile Include="..\DaemonWin32.cpp" />
@@ -32,6 +32,7 @@
<ClCompile Include="..\I2NPProtocol.cpp" />
<ClCompile Include="..\i2p.cpp" />
<ClCompile Include="..\I2PEndian.cpp" />
<ClCompile Include="..\I2PService.cpp" />
<ClCompile Include="..\Identity.cpp" />
<ClCompile Include="..\LeaseSet.cpp" />
<ClCompile Include="..\Log.cpp" />
@@ -57,13 +58,14 @@
<ClCompile Include="..\util.cpp" />
<ClCompile Include="..\SOCKS.cpp" />
<ClCompile Include="..\I2PTunnel.cpp" />
<ClCompile Include="..\I2PControl.cpp" />
<ClCompile Include="..\ClientContext.cpp" />
<ClCompile Include="Win32Service.cpp" />
</ItemGroup>
<ItemGroup>
<ClInclude Include="..\AddressBook.h" />
<ClInclude Include="..\base64.h" />
<ClInclude Include="..\BOB.h" />
<ClInclude Include="..\BOB.h" />
<ClInclude Include="..\CryptoConst.h" />
<ClInclude Include="..\Daemon.h" />
<ClInclude Include="..\ElGamal.h" />
@@ -72,6 +74,7 @@
<ClInclude Include="..\HTTPServer.h" />
<ClInclude Include="..\I2NPProtocol.h" />
<ClInclude Include="..\I2PEndian.h" />
<ClInclude Include="..\I2PService.h" />
<ClInclude Include="..\Identity.h" />
<ClInclude Include="..\LeaseSet.h" />
<ClInclude Include="..\LittleBigEndian.h" />
@@ -102,6 +105,7 @@
<ClInclude Include="..\util.h" />
<ClInclude Include="..\SOCKS.h" />
<ClInclude Include="..\I2PTunnel.h" />
<ClInclude Include="..\I2PControl.h" />
<ClInclude Include="..\version.h" />
<ClInclude Include="..\Signature.h" />
<ClInclude Include="..\ClientContext.h" />
@@ -280,4 +284,4 @@
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
<ImportGroup Label="ExtensionTargets">
</ImportGroup>
</Project>
</Project>

View File

@@ -135,6 +135,15 @@
<ClCompile Include="..\ClientContext.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="..\BOB.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="..\I2PControl.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="..\I2PService.cpp">
<Filter>Source Files</Filter>
</ClCompile>
</ItemGroup>
<ItemGroup>
<ClInclude Include="..\Identity.h">
@@ -275,6 +284,15 @@
<ClInclude Include="resource.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="..\BOB.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="..\I2PControl.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="..\I2PService.h">
<Filter>Header Files</Filter>
</ClInclude>
</ItemGroup>
<ItemGroup>
<ResourceCompile Include="Resource.rc">

View File

@@ -39,11 +39,11 @@ namespace api
{
LogPrint("Shutdown started.");
i2p::tunnel::tunnels.Stop();
LogPrint("Tunnels stoped");
LogPrint("Tunnels stopped");
i2p::transport::transports.Stop();
LogPrint("Transports stoped");
LogPrint("Transports stopped");
i2p::data::netdb.Stop();
LogPrint("NetDB stoped");
LogPrint("NetDB stopped");
StopLog ();
}
@@ -85,7 +85,7 @@ namespace api
auto leaseSet = dest->FindLeaseSet (remote);
if (leaseSet)
{
auto stream = dest->CreateStream (*leaseSet);
auto stream = dest->CreateStream (leaseSet);
stream->Send (nullptr, 0); // connect
return stream;
}

View File

@@ -48,6 +48,8 @@ set (DAEMON_SRC
"${CMAKE_SOURCE_DIR}/Daemon.cpp"
"${CMAKE_SOURCE_DIR}/HTTPProxy.cpp"
"${CMAKE_SOURCE_DIR}/HTTPServer.cpp"
"${CMAKE_SOURCE_DIR}/I2PService.cpp"
"${CMAKE_SOURCE_DIR}/I2PControl.cpp"
"${CMAKE_SOURCE_DIR}/I2PTunnel.cpp"
"${CMAKE_SOURCE_DIR}/SAM.cpp"
"${CMAKE_SOURCE_DIR}/SOCKS.cpp"

View File

@@ -8,7 +8,7 @@ i2p_SOURCES = AddressBook.cpp CryptoConst.cpp Daemon.cpp \
SSUData.cpp Streaming.cpp TransitTunnel.cpp \
Transports.cpp Tunnel.cpp TunnelEndpoint.cpp \
TunnelGateway.cpp TunnelPool.cpp UPnP.cpp aes.cpp \
base64.cpp i2p.cpp util.cpp \
base64.cpp i2p.cpp util.cpp I2PService.cpp \
\
AddressBook.h CryptoConst.h Daemon.h ElGamal.h \
Garlic.h HTTPProxy.h HTTPServer.h I2NPProtocol.h \
@@ -19,7 +19,7 @@ i2p_SOURCES = AddressBook.cpp CryptoConst.cpp Daemon.cpp \
TransitTunnel.h Transports.h Tunnel.h TunnelBase.h \
TunnelConfig.h TunnelEndpoint.h TunnelGateway.h \
TunnelPool.h UPnP.h aes.h base64.h config.h hmac.h \
util.h version.h
util.h version.h I2PService.h
AM_LDFLAGS = @BOOST_DATE_TIME_LIB@ @BOOST_FILESYSTEM_LIB@ \
@BOOST_PROGRAM_OPTIONS_LIB@ @BOOST_REGEX_LIB@ \

View File

@@ -115,7 +115,8 @@ am_i2p_OBJECTS = AddressBook.$(OBJEXT) CryptoConst.$(OBJEXT) \
TunnelGateway.$(OBJEXT) TunnelPool.$(OBJEXT) UPnP.$(OBJEXT) \
aes.$(OBJEXT) base64.$(OBJEXT) i2p.$(OBJEXT) util.$(OBJEXT) \
SAM.$(OBJEXT) Destination.$(OBJEXT) ClientContext.$(OBJEXT) \
Datagram.$(OBJEXT) SSUSession.$(OBJEXT) BOB.$(OBJEXT)
Datagram.$(OBJEXT) SSUSession.$(OBJEXT) BOB.$(OBJEXT) \
I2PControl.$(OBJEXT)
i2p_OBJECTS = $(am_i2p_OBJECTS)
i2p_LDADD = $(LDADD)
AM_V_P = $(am__v_P_@AM_V@)
@@ -326,7 +327,8 @@ i2p_SOURCES = AddressBook.cpp CryptoConst.cpp Daemon.cpp \
Transports.cpp Tunnel.cpp TunnelEndpoint.cpp \
TunnelGateway.cpp TunnelPool.cpp UPnP.cpp aes.cpp \
base64.cpp i2p.cpp util.cpp SAM.cpp Destination.cpp \
ClientContext.cpp DataFram.cpp SSUSession.cpp BOB.cpp \
ClientContext.cpp DataFram.cpp SSUSession.cpp BOB.cpp \
I2PControl.cpp \
\
AddressBook.h CryptoConst.h Daemon.h ElGamal.h \
Garlic.h HTTPProxy.h HTTPServer.h I2NPProtocol.h \
@@ -338,7 +340,8 @@ i2p_SOURCES = AddressBook.cpp CryptoConst.cpp Daemon.cpp \
TunnelConfig.h TunnelEndpoint.h TunnelGateway.h \
TunnelPool.h UPnP.h aes.h base64.h config.h hmac.h \
util.h version.h Destination.h ClientContext.h \
TransportSession.h Datagram.h SSUSession.h BOB.h
TransportSession.h Datagram.h SSUSession.h BOB.h \
I2PControl.h
AM_LDFLAGS = @BOOST_DATE_TIME_LIB@ @BOOST_FILESYSTEM_LIB@ \
@BOOST_PROGRAM_OPTIONS_LIB@ @BOOST_REGEX_LIB@ \

View File

@@ -0,0 +1,32 @@
-----BEGIN CERTIFICATE-----
MIIFhTCCA22gAwIBAgIEeCJXkjANBgkqhkiG9w0BAQ0FADBzMQswCQYDVQQGEwJY
WDELMAkGA1UECBMCWFgxCzAJBgNVBAcTAlhYMR4wHAYDVQQKExVJMlAgQW5vbnlt
b3VzIE5ldHdvcmsxDDAKBgNVBAsTA0kyUDEcMBoGA1UEAwwTY2hlZXp5YnVkekBt
YWlsLmkycDAeFw0xNDEyMTYyMTU3MTZaFw0yNDEyMTUyMTU3MTZaMHMxCzAJBgNV
BAYTAlhYMQswCQYDVQQIEwJYWDELMAkGA1UEBxMCWFgxHjAcBgNVBAoTFUkyUCBB
bm9ueW1vdXMgTmV0d29yazEMMAoGA1UECxMDSTJQMRwwGgYDVQQDDBNjaGVlenli
dWR6QG1haWwuaTJwMIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEAgLkj
Jsp8pjRi5N/JHHz+MXisgbI9G0vpd3yDhHvae3oF87iiQbxflcdcoH0l5RZL0cAn
w4amhqoOk2qhf+NSAEkiPWhk7CzPBRwDExEM/gmHYLWXbfnoHGaEls9ORGuDlDmN
hCFJVrxaZocIOi/7gZ4A+tC8wq+1aoe0Yhr381OW59w9AdUAWjBWibO3V59dEklL
7HqfOc2v7AMKDLJWgPekj8ZbqA9lRxHM6Djtjz4d9QXeQa8j3xLXTX1QbkvJBBX1
9Rzi/Nzv622lzoZ/Z/61jW7Bz+h9aJ6qp4on9K4ygUw/VduTOH/1ryQmw87x4MFQ
Z/Y86lOl7XZxBjtpYpGQW/5LmBe2BCfWgIYe9N5ionNgAe5TNEIDngP9AvJmcTyF
KcGgOgXQO9EeHEdgf4nC6RbGrb2sBtRjWJv5nOhHRG9tpwYkw/Zc5ZNHOymYpPMg
wce3me+1psJFt+gXhDcvxpRgTZpXfz91K/nKt3+szcYFluqhJLi6nL1TmXQVn51X
lGD1bcy1VUof+uKyb223JX5rm9WQ48GzUfy5cK4o+khEo0RLb21FwG5iJwVzhtoN
xQS1TO6pwLn8Si1ePRwntzlOm8DPIwdUkPBQNJ9DDkcdVia2GgbVM6LH8lrukekq
soYfwmOTsFRkGo04ujDI/IeMrl3zmJphyQkGx18CAwEAAaMhMB8wHQYDVR0OBBYE
FJ2MHeHnfCpEuYvC/9eK2ML9ne2eMA0GCSqGSIb3DQEBDQUAA4ICAQA3XUS7Zw1i
RJWPSu2oLzV7oTtIW5po2Gd5BL3oU6BvlK1zLw/z/soF/LopeHQudBYxYckyv4MG
gTNS9fcKkVdhNyLI/R2S0nQ/VFhTzuvq8HnnTOpvopA/cXTQlgrhGB2ajIZMYsXe
lei0V5H23etXTbYZWK6/IgoALk5vowde9lpJEIBhupIafqFg0tAo4LX07/eNxDOp
nXrShsYhHNaRhglS+0Gqj1UK0WvgMJxQKJm/VLi7jx8vfRkqXs/b76XT+VMQuUJd
l5llQwpOicQhX/ZTAO+iWrDaO7mz/ZDweLxnfWd3m2JwDJlE9K5l98zdcve96NRZ
ePnK8vBoAPQ9iHhwFSC5GpirK1KmT/BDLjqEF3H/HgPdPWSh97AUFpBryEIdZk1q
Czi9DCvwHNpbpI20Fo48+2N7sbvq4onZZqx5V0SjTj/9bHSSDwG9ok1JqWoZmRvo
p4MIywAJowlvPNc++jSHT3R7segeNUi/UdYCmm70j1av+0aEknmvPtF6atsHJ22X
5OMBhiPi1pudFWFJFWk4WOjrK/juwHHfHNgFVyziva4q6wPKrPno0gO5pCpfRUld
QAoSPgo8LAB3dugt5Xfsuone2GhLi1SLQlWFJWHswd/ypWa0FB+xn6Edkc1noOWY
06dwfEP/gTCAnSplLyrFWxnyHManBxq/bQ==
-----END CERTIFICATE-----

View File

@@ -1,31 +0,0 @@
-----BEGIN CERTIFICATE-----
MIIFVjCCAz6gAwIBAgIEU71jgDANBgkqhkiG9w0BAQ0FADBtMQswCQYDVQQGEwJY
WDELMAkGA1UECBMCWFgxCzAJBgNVBAcTAlhYMR4wHAYDVQQKExVJMlAgQW5vbnlt
b3VzIE5ldHdvcmsxDDAKBgNVBAsTA0kyUDEWMBQGA1UEAwwNc3dhdEBtYWlsLmky
cDAeFw0xNDA3MDkxNTQ1MDRaFw0yNDA3MDgxNTQ1MDRaMG0xCzAJBgNVBAYTAlhY
MQswCQYDVQQIEwJYWDELMAkGA1UEBxMCWFgxHjAcBgNVBAoTFUkyUCBBbm9ueW1v
dXMgTmV0d29yazEMMAoGA1UECxMDSTJQMRYwFAYDVQQDDA1zd2F0QG1haWwuaTJw
MIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEAjSj53hsbTqtzbnlf5LbR
HmfdC2br9QZaB9e5IQKprlTptdzqTrt2LRS6ZaJ06BKJgX3AfLflvyeUDUPoyg63
I9a1kb1AsrcxvMkHXTUwPaO09caO5/CiQ3zx/iuTl4e0MmGe3cz7jsvZNdOVH0ba
B691GB2UBq2QjobGx01qjWtACCmoQIcEur2ns/l+VzAextBL70dSPN6EJAonQnWc
JvHf1vhXp5DWasaIovm8haNo48QpCo7NllsAjiONQM9rrJITvzFG9hX9cv/B1Kr4
LCebTXv58ViXXFsnxYhktAFwP33fn1eCLraJ/BpaDR4+s3ovMC/7S+g5//+sQTd1
pR/kXx4BmZWZdzs083Z/2skQON75j/qnMhUQqpFCqUImm4lOhlIGbzFJ4GQM6VCR
V4BbvC3XuDc6vlivLzWpUEU7Kc6YnfGgi2G//ZCj2CAoR7qZs/n9997C8oAvGY5z
XGVC/GqIFHuFvnDfPDvxGovYjLJ0KrNtAmp2Rb5812glnVdPwbRYRUBg3ICbNey3
rmGPURDq0aHMTzX4gtM+/hCYYVnkzNMQvYuw9EZLZrK/XdM1a0U4kajZSKKJsTmW
uQwXSUVjTKQh//yL4zPoELucFk5r7apLePEm6aCeWuY2wVkR8KEFgNanwDckWQAm
Lk9r2t+Y/l2jS2NqBWFyr+cCAwEAATANBgkqhkiG9w0BAQ0FAAOCAgEAgFSquj/0
iZYpFI1XarSIVpGMo0WLAmb9GZCn5yoXSeE6eypI/hHhXA4Bjdk2Ae33pXPNcCV8
oT/gHj8943Wx7CTxty2zHzIsd92/HG2EG6U/HPp5l7yIJQaWoe/9tjQoaBhipZOK
+MiytkoBWkyXFqXnKQPExiadWB8axHtt66vrikOcSx6Ur3u5DPKybvY4fsuvo4+I
cLLgoueFm6I1WhmkVmjtm4k2yZ/Z3NEYjg52rv8NuYhRwK2JrQeRzMZv/zt5KhOt
05woHrzymjfFBu0M8uxX7EGZBIsc8zcEY7JL/NSMArw/QCgLU5bQF6+CsyxWUkt1
obMRXU1oS9GjC/1F0kw52NOz2qzBn9tZBc1zs8+GLpYBUf9KiUMFOfJpkr706VqC
orgxRYwncicq+de2PlesxJb3DNPFuAzUNzAqxcVYDoFPAiL1zCEl0nhBrbN+x93X
ojTfV3UlbMjMkQKveYJxsi5/+jO1dHIkXpzK4bwFwHmJ2RCa6PualWhuXldX6mR+
APoY6xeoPRlyKk+POrSwU+hywUudyPuFyzDMo8n1w4CyqL+/ky3YsLfGBM1phbb2
GEnZ0J1HW34Pnie1rzaCak+3RfaZsImCwh1xXl/H7Ka9bLeUIfOuipSSroctdaiG
84wIiEjxgjW2ldM37gTX2XtE/blB1YPIZ5U=
-----END CERTIFICATE-----

View File

@@ -0,0 +1,31 @@
-----BEGIN CERTIFICATE-----
MIIFVDCCAzwCCQC2r1XWYtqtAzANBgkqhkiG9w0BAQsFADBsMQswCQYDVQQGEwJY
WDELMAkGA1UECAwCWFgxCzAJBgNVBAcMAlhYMRMwEQYDVQQKDApQdXJwbGUgSTJQ
MQ0wCwYDVQQLDARJMlBEMR8wHQYJKoZIhvcNAQkBFhBvcmlnbmFsQG1haWwuaTJw
MB4XDTE1MDIyMjEzNTgxOFoXDTI1MDIxOTEzNTgxOFowbDELMAkGA1UEBhMCWFgx
CzAJBgNVBAgMAlhYMQswCQYDVQQHDAJYWDETMBEGA1UECgwKUHVycGxlIEkyUDEN
MAsGA1UECwwESTJQRDEfMB0GCSqGSIb3DQEJARYQb3JpZ25hbEBtYWlsLmkycDCC
AiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBALp3D/gdvFjrMm+IE8tHZCWE
hQ6Pp0CCgCGDBC3WQFLqR98bqVPl4UwRG/MKY/LY7Woai06JNmGcpfw0LMoNnHxT
bvKtDRe/8kQdhdLHhgIkWKSbMvTAl7uUdV6FzsPgDR0x7scoFVWEhkF0wfmzGF2V
yr/WCBQejFPu69z03m5tRQ8Xjp2txWV45RawUmFu50bgbZvLCSLfTkIvxmfJzgPN
pJ3sPa/g7TBZl2uEiAu4uaEKvTuuzStOWCGgFaHYFVlTfFXTvmhFMqHfaidtzrlu
H35WGrmIWTDl6uGPC5QkSppvkj73rDj5aEyPzWMz5DN3YeECoVSchN+OJJCM6m7+
rLFYXghVEp2h+T9O1GBRfcHlQ2E3CrWWvxhmK8dfteJmd501dyNX2paeuIg/aPFO
54/8m2r11uyF29hgY8VWLdXtqvwhKuK36PCzofEwDp9QQX8GRsEV4pZTrn4bDhGo
kb9BF7TZTqtL3uyiRmIyBXrNNiYlA1Xm4fyKRtxl0mrPaUXdgdnCt3KxOAJ8WM2B
7L/kk9U8C/nexHbMxIZfTap49XcUg5dxSO9kOBosIOcCUms8sAzBPDV2tWAByhYF
jI/Tutbd3F0+fvcmTcIFOlGbOxKgO2SfwXjv/44g/3LMK6IAMFB9UOc8KhnnJP0f
uAHvMXn1ahRs4pM1VizLAgMBAAEwDQYJKoZIhvcNAQELBQADggIBAIOxdaXT+wfu
nv/+1hy5T4TlRMNNsuj79ROcy6Mp+JwMG50HjTc0qTlXh8C7nHybDJn4v7DA+Nyn
RxT0J5I+Gqn+Na9TaC9mLeX/lwe8/KomyhBWxjrsyWj1V6v/cLO924S2rtcfzMDm
l3SFh9YHM1KF/R9N1XYBwtMzr3bupWDnE1yycYp1F4sMLr5SMzMQ0svQpQEM2/y5
kly8+eUzryhm+ag9x1686uEG5gxhQ1eHQoZEaClHUOsV+28+d5If7cqcYx9Hf5Tt
CiVjJQzdxBF+6GeiJtKxnLtevqlkbyIJt6Cm9/7YIy/ovRGF2AKSYN6oCwmZQ6i1
8nRnFq5zE7O94m+GXconWZxy0wVqA6472HThMi7S+Tk/eLYen2ilGY+KCb9a0FH5
5MOuWSoJZ8/HfW2VeQmL8EjhWm5F2ybg28wgXK4BOGR3jQi03Fsc+AFidnWxSKo0
aiJoPgOsfyu8/fnCcAi07kSmjzUKIWskApgcpGQLNXHFK9mtg7+VA8esRnfLlKtP
tJf+nNAPY1sqHfGBzh7WWGWal5RGHF5nEm3ta3oiFF5sMKCJ6C87zVwFkEcRytGC
xOGmiG1O1RPrO5NG7rZUaQ4y1OKl2Y1H+nGONzZ3mvoAOvxEq6JtUnU2kZscpPlk
fpeOSDoGBYJGbIpzDreBDhxaZrwGq36k
-----END CERTIFICATE-----

View File

@@ -1,44 +0,0 @@
-----BEGIN CERTIFICATE-----
MIIHyTCCBbGgAwIBAgIBATANBgkqhkiG9w0BAQUFADB9MQswCQYDVQQGEwJJTDEW
MBQGA1UEChMNU3RhcnRDb20gTHRkLjErMCkGA1UECxMiU2VjdXJlIERpZ2l0YWwg
Q2VydGlmaWNhdGUgU2lnbmluZzEpMCcGA1UEAxMgU3RhcnRDb20gQ2VydGlmaWNh
dGlvbiBBdXRob3JpdHkwHhcNMDYwOTE3MTk0NjM2WhcNMzYwOTE3MTk0NjM2WjB9
MQswCQYDVQQGEwJJTDEWMBQGA1UEChMNU3RhcnRDb20gTHRkLjErMCkGA1UECxMi
U2VjdXJlIERpZ2l0YWwgQ2VydGlmaWNhdGUgU2lnbmluZzEpMCcGA1UEAxMgU3Rh
cnRDb20gQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkwggIiMA0GCSqGSIb3DQEBAQUA
A4ICDwAwggIKAoICAQDBiNsJvGxGfHiflXu1M5DycmLWwTYgIiRezul38kMKogZk
pMyONvg45iPwbm2xPN1yo4UcodM9tDMr0y+v/uqwQVlntsQGfQqedIXWeUyAN3rf
OQVSWff0G0ZDpNKFhdLDcfN1YjS6LIp/Ho/u7TTQEceWzVI9ujPW3U3eCztKS5/C
Ji/6tRYccjV3yjxd5srhJosaNnZcAdt0FCX+7bWgiA/deMotHweXMAEtcnn6RtYT
Kqi5pquDSR3l8u/d5AGOGAqPY1MWhWKpDhk6zLVmpsJrdAfkK+F2PrRt2PZE4XNi
HzvEvqBTViVsUQn3qqvKv3b9bZvzndu/PWa8DFaqr5hIlTpL36dYUNk4dalb6kMM
Av+Z6+hsTXBbKWWc3apdzK8BMewM69KN6Oqce+Zu9ydmDBpI125C4z/eIT574Q1w
+2OqqGwaVLRcJXrJosmLFqa7LH4XXgVNWG4SHQHuEhANxjJ/GP/89PrNbpHoNkm+
Gkhpi8KWTRoSsmkXwQqQ1vp5Iki/untp+HDH+no32NgN0nZPV/+Qt+OR0t3vwmC3
Zzrd/qqc8NSLf3Iizsafl7b4r4qgEKjZ+xjGtrVcUjyJthkqcwEKDwOzEmDyei+B
26Nu/yYwl/WL3YlXtq09s68rxbd2AvCl1iuahhQqcvbjM4xdCUsT37uMdBNSSwID
AQABo4ICUjCCAk4wDAYDVR0TBAUwAwEB/zALBgNVHQ8EBAMCAa4wHQYDVR0OBBYE
FE4L7xqkQFulF2mHMMo0aEPQQa7yMGQGA1UdHwRdMFswLKAqoCiGJmh0dHA6Ly9j
ZXJ0LnN0YXJ0Y29tLm9yZy9zZnNjYS1jcmwuY3JsMCugKaAnhiVodHRwOi8vY3Js
LnN0YXJ0Y29tLm9yZy9zZnNjYS1jcmwuY3JsMIIBXQYDVR0gBIIBVDCCAVAwggFM
BgsrBgEEAYG1NwEBATCCATswLwYIKwYBBQUHAgEWI2h0dHA6Ly9jZXJ0LnN0YXJ0
Y29tLm9yZy9wb2xpY3kucGRmMDUGCCsGAQUFBwIBFilodHRwOi8vY2VydC5zdGFy
dGNvbS5vcmcvaW50ZXJtZWRpYXRlLnBkZjCB0AYIKwYBBQUHAgIwgcMwJxYgU3Rh
cnQgQ29tbWVyY2lhbCAoU3RhcnRDb20pIEx0ZC4wAwIBARqBl0xpbWl0ZWQgTGlh
YmlsaXR5LCByZWFkIHRoZSBzZWN0aW9uICpMZWdhbCBMaW1pdGF0aW9ucyogb2Yg
dGhlIFN0YXJ0Q29tIENlcnRpZmljYXRpb24gQXV0aG9yaXR5IFBvbGljeSBhdmFp
bGFibGUgYXQgaHR0cDovL2NlcnQuc3RhcnRjb20ub3JnL3BvbGljeS5wZGYwEQYJ
YIZIAYb4QgEBBAQDAgAHMDgGCWCGSAGG+EIBDQQrFilTdGFydENvbSBGcmVlIFNT
TCBDZXJ0aWZpY2F0aW9uIEF1dGhvcml0eTANBgkqhkiG9w0BAQUFAAOCAgEAFmyZ
9GYMNPXQhV59CuzaEE44HF7fpiUFS5Eyweg78T3dRAlbB0mKKctmArexmvclmAk8
jhvh3TaHK0u7aNM5Zj2gJsfyOZEdUauCe37Vzlrk4gNXcGmXCPleWKYK34wGmkUW
FjgKXlf2Ysd6AgXmvB618p70qSmD+LIU424oh0TDkBreOKk8rENNZEXO3SipXPJz
ewT4F+irsfMuXGRuczE6Eri8sxHkfY+BUZo7jYn0TZNmezwD7dOaHZrzZVD1oNB1
ny+v8OqCQ5j4aZyJecRDjkZy42Q2Eq/3JR44iZB3fsNrarnDy0RLrHiQi+fHLB5L
EUTINFInzQpdn4XBidUaePKVEFMy3YCEZnXZtWgo+2EuvoSoOMCZEoalHmdkrQYu
L6lwhceWD3yJZfWOQ1QOq92lgDmUYMA0yZZwLKMS9R9Ie70cfmu3nZD0Ijuu+Pwq
yvqCUqDvr0tVk+vBtfAii6w0TiYiBKGHLHVKt+V9E9e4DGTANtLJL4YSjCMJwRuC
O3NJo2pXh5Tl1njFmUNj403gdy3hZZlyaQQaRwnmDwFWJPsfvw55qVguucQJAX6V
um0ABj6y6koQOdjQK/W/7HW/lwLFCRsI3FU34oH7N4RDYiDK51ZLZer+bMEkkySh
NOsF/5oirpt9P/FlUQqmMGqz9IgcgA38corog14=
-----END CERTIFICATE-----

View File

@@ -1,21 +0,0 @@
-----BEGIN CERTIFICATE-----
MIIDhTCCAm2gAwIBAgIJAPVgXcMcr3zqMA0GCSqGSIb3DQEBBQUAMFkxCzAJBgNV
BAYTAkVVMQ8wDQYDVQQIDAZFdXJvcGUxDDAKBgNVBAoMA0kyUDETMBEGA1UECwwK
T3V0cHJveGllczEWMBQGA1UEAwwNaTJwLmZlYXJlZC5ldTAeFw0xMjEwMjkxNzMw
MDZaFw0yMTAxMTUxNzMwMDZaMFkxCzAJBgNVBAYTAkVVMQ8wDQYDVQQIDAZFdXJv
cGUxDDAKBgNVBAoMA0kyUDETMBEGA1UECwwKT3V0cHJveGllczEWMBQGA1UEAwwN
aTJwLmZlYXJlZC5ldTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAOUh
y2+6Q4RO+b5WPXX/cZ/9fiI7aWGe/C7z0083HOEqnkgGCYgxFWUCed6/eZbYoZ7/
PV1BAuEereNwTp+Ov7fQB2H73O9sSAEejW6O4C2PZiZWaPxpZiTJNENbLOZxJnIN
+fSqmA5pqvGkYAJ2heZH4v4tayun7Vib58GWuizhzJ4EvhOrOrLq/YHrxMn++r4e
kNNbq4QzWpfxNa7ocDY9OJh5qFzuc+6wKj1m1syK6euDqs5d6X+y0aDTMgRxey2b
tkmNx9wC0flLg1oMcv9o1zN+dENy7Inkd/SqbSjLUqDTJzdq6xURVsgLoV63pb6r
B4gbGIlriYWK/mOPTTkCAwEAAaNQME4wHQYDVR0OBBYEFOI94JZ3Rb2RVmr8QjOp
u3KfVSrNMB8GA1UdIwQYMBaAFOI94JZ3Rb2RVmr8QjOpu3KfVSrNMAwGA1UdEwQF
MAMBAf8wDQYJKoZIhvcNAQEFBQADggEBAD7bI05zg9nf9qanq4ZNw/rvEzYQRBmy
MqzZjcwBMGvbcEbS+zYAdAkfxmN3l/AT4I4z138Om0ud4ZJUQTVlRsJkMlmLD4Rt
Jbi2rl7mrY7Qupgu5hvgH+ZaEWr7LTq+tFjPycRS+zijw9NToKeAsgEex9zYIOYD
BxDUn/trvyA41ItvegWh803IsZUBb45Via+bopid9aFFkejRrck9hhcQ6fVh2yju
nuVwHrxNvGc0NmmJ7zI+nPESFS+TAYbWXikDhc5Vtyiuoz47WZU1cgXYYMejK4WA
+3GLvei7qKm4GOJSg7BngF5Iyj/n7ML1rBqTlN3KA1YOgpGCwJlKzto=
-----END CERTIFICATE-----

View File

@@ -0,0 +1,23 @@
-----BEGIN CERTIFICATE-----
MIIDvTCCAqWgAwIBAgIJAOeW0ejPrHimMA0GCSqGSIb3DQEBCwUAMHUxCzAJBgNV
BAYTAlVTMQ0wCwYDVQQIDARub25lMQ0wCwYDVQQHDARub25lMQ0wCwYDVQQKDARu
b25lMQ0wCwYDVQQLDARub25lMRUwEwYDVQQDDAxpMnAubW9vby5jb20xEzARBgkq
hkiG9w0BCQEWBG5vbmUwHhcNMTUwMjA4MTczMzA5WhcNMTkwMzE5MTczMzA5WjB1
MQswCQYDVQQGEwJVUzENMAsGA1UECAwEbm9uZTENMAsGA1UEBwwEbm9uZTENMAsG
A1UECgwEbm9uZTENMAsGA1UECwwEbm9uZTEVMBMGA1UEAwwMaTJwLm1vb28uY29t
MRMwEQYJKoZIhvcNAQkBFgRub25lMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIB
CgKCAQEAqxej7oRl9GOb8benIBCENrJXoow1iWhI9M+2nU0SaonrCDql5M2YMlwd
HzYUWtFbRjz2NinjB0fgFq9cfzHfr1Sc8k/OeGg1jvNfqt8wWo9tryQNjiHtDQUZ
6lQ5T13I+lj0CBasowgbApKQfrYjvaeuTaVYTfP8IVA60hoUQ+sy9JN+Unsx3/0Y
PLLd98+bT27qYuBNRB1g/ifUTd9Wosj2PevGBlCxYDaUjmCG4Q8kcQr87KvM6RTu
3AV61s/Wyy1j2YemlGG/ZhJ44YnlVMSu1vTjt9HInVf3lRRx/+RzbQO3lqeVC8LC
Bq3KbSlfJVx4vHslfHwBFw9A4rmD1QIDAQABo1AwTjAdBgNVHQ4EFgQUsSUvX0ED
yivB67iksVwZ+b8vLtQwHwYDVR0jBBgwFoAUsSUvX0EDyivB67iksVwZ+b8vLtQw
DAYDVR0TBAUwAwEB/zANBgkqhkiG9w0BAQsFAAOCAQEAde4wts7Q8TylFEc38ftJ
2f285fFIR7P1SSbBcHPK2eBwLEg0zJyFrCeiHuEpPrn+d5GqL2zOskjfcESGmDBT
aFajj8jPBJj/AmpkdWJG6a1YKro5tu9wrlenGwHOHu2/Cl0IJvafxrOs2x4G+2Nl
5Hcw/FIy8mK7eIch4pACfi0zNMZ6KMCKfX9bxPrQo78WdBfVjbrIBlgyOQJ5NJEF
JlWvS7Butv7eERi4I2huN5VRJSCFzjbuO+tjP3I8IB6WgdBmTeqq8ObtXRgahBuD
ZmkvqVSfIzK5JN4GjO8FOdCBomuwm9A92kgmAptwQwAHM9qCDJpH8L07/7poxlGb
iA==
-----END CERTIFICATE-----

View File

@@ -0,0 +1,27 @@
-----BEGIN CERTIFICATE-----
MIIEkzCCA3ugAwIBAgIJAKsW7idQxp0aMA0GCSqGSIb3DQEBCwUAMIHfMQswCQYD
VQQGEwJVSzEgMB4GA1UECAwXaTJwc2VlZC56YXJyZW5zcHJ5LmluZm8xIDAeBgNV
BAcMF2kycHNlZWQuemFycmVuc3ByeS5pbmZvMSAwHgYDVQQKDBdpMnBzZWVkLnph
cnJlbnNwcnkuaW5mbzEgMB4GA1UECwwXaTJwc2VlZC56YXJyZW5zcHJ5LmluZm8x
IDAeBgNVBAMMF2kycHNlZWQuemFycmVuc3ByeS5pbmZvMSYwJAYJKoZIhvcNAQkB
FhdpMnBzZWVkLnphcnJlbnNwcnkuaW5mbzAeFw0xNDEyMjgxOTI3MDdaFw0xOTAy
MDUxOTI3MDdaMIHfMQswCQYDVQQGEwJVSzEgMB4GA1UECAwXaTJwc2VlZC56YXJy
ZW5zcHJ5LmluZm8xIDAeBgNVBAcMF2kycHNlZWQuemFycmVuc3ByeS5pbmZvMSAw
HgYDVQQKDBdpMnBzZWVkLnphcnJlbnNwcnkuaW5mbzEgMB4GA1UECwwXaTJwc2Vl
ZC56YXJyZW5zcHJ5LmluZm8xIDAeBgNVBAMMF2kycHNlZWQuemFycmVuc3ByeS5p
bmZvMSYwJAYJKoZIhvcNAQkBFhdpMnBzZWVkLnphcnJlbnNwcnkuaW5mbzCCASIw
DQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBANrEncHwS+7R0Ti/jZa2Ex7ujglV
huYO59nLxeAOpEQwn6V41X5+L0hmhM0zuYavuP1jzKfF/Cn0CG1PqkGbEnXrTOGf
4gMj2wy/UVVFXaPQwldi+CEiNo6nI5S+T/upg5VK6M5/ahYbfIbX5xF27QNPV5qW
RnM0VK4gIQkFFtpiI0dFcEU9VYe+cg7a4Jvxc5LzqaIBZHWMX6alPfBT70LkYiiQ
76IRw5oBmqZjfIdiudRhFkezMkDomKSgLR2/0HJbekq2WeLXJLMPM1rdpCYldBEi
t6Zng9uAJa1mA6Al4RhO1aQEPj9Vo5h+Vj6FHJAJJcb+YW6wLKBkJVGLF4UCAwEA
AaNQME4wHQYDVR0OBBYEFL538Fr1l/9YQgG+iZvJUuOzAaVaMB8GA1UdIwQYMBaA
FL538Fr1l/9YQgG+iZvJUuOzAaVaMAwGA1UdEwQFMAMBAf8wDQYJKoZIhvcNAQEL
BQADggEBAKq7KEnR0V43PsA5D23Lhawy5W/BDs4RO3LkYSxi+zR4EAMC8RhafrmG
6IZVp+ykplZtFK3Kkw1osakcvmHRLoPCXPWLibXtWMEpmH4GhWJKf5Ct1kY0VkEE
ALP7vCtjDm5l6WBaNOZYv25wwg5wgjyhzfJtLxzyRRPOjUuv0M3FFwJEAauzoo+4
nle91IHNcWPIq1kgWUwWBHpLgZ2RpSOZS9MBOCkjHwQhoebhpgwSPgUHvBJ7FoLb
AeAdwpgPdIQ9gZEZEPfCPfG/Qp60yLAhkT2CF7F1h47VYe8LGBDbd1HGpSwjulq/
lnvV4zDIoKhbQhUpxwgHo79nxcgddOA=
-----END CERTIFICATE-----

View File

@@ -0,0 +1,23 @@
-----BEGIN CERTIFICATE-----
MIID1TCCAr2gAwIBAgIJAOd9wIt+w/I5MA0GCSqGSIb3DQEBCwUAMIGAMQswCQYD
VQQGEwJOTzENMAsGA1UECAwET3NsbzENMAsGA1UEBwwET3NsbzEMMAoGA1UECgwD
STJQMQwwCgYDVQQLDANJMlAxFjAUBgNVBAMMDW5ldGRiLmkycDIubm8xHzAdBgkq
hkiG9w0BCQEWEG1lZWhAaTJwbWFpbC5vcmcwHhcNMTQxMjA2MjM1OTM1WhcNMjAw
NTI4MjM1OTM1WjCBgDELMAkGA1UEBhMCTk8xDTALBgNVBAgMBE9zbG8xDTALBgNV
BAcMBE9zbG8xDDAKBgNVBAoMA0kyUDEMMAoGA1UECwwDSTJQMRYwFAYDVQQDDA1u
ZXRkYi5pMnAyLm5vMR8wHQYJKoZIhvcNAQkBFhBtZWVoQGkycG1haWwub3JnMIIB
IjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAmtRtAALMImh0G0X+AtMpJNBa
HduNkg5t+0juitKRboXXAp5k7yN9qnimlBxlAicNb+QubcDuL+WV91NKz43dd6Xp
SAewqMFRPUAki8uYzoh+hQEfzyd3NmadUKquYZsYwomhHnraOmLZLbxD6ED3FEwl
hGBJwYnhyMZUCgB5+DEEHg8RdLz+H0bMrwz3e7/0lMtH6lM1lIHz0KBULWLp7Om0
sk3rmmhPUIXqfoY8X3vClI74o0KcslMVaF4rt3lAHdoi3lwA6Qbdqq9nC9rPWHUS
USQQ/MKsNfDTGsHkbW2l0VgNvJkw92DwHTXSJrsEqgkdV/B1hHxCKgL44c/CbwID
AQABo1AwTjAdBgNVHQ4EFgQUCkebDZE05yKMbXORa6gO+aLdCscwHwYDVR0jBBgw
FoAUCkebDZE05yKMbXORa6gO+aLdCscwDAYDVR0TBAUwAwEB/zANBgkqhkiG9w0B
AQsFAAOCAQEAfHO0g5M//X5xDIuXGCeqQMUrF3r1N45a+0kqo2b/rd9USueNGrJl
KE7MfDgShy2d4strZ1m0M4StW0RlUUZ4V4FYwzcknF6VXbOQK3BTrAeOwuxsrHoT
abrMZ36ABYur5WakOYtPyQ5oXFUAIpGBe9LH7q3XLegSOfftvc2xdJ+VK0n4MEfY
GfaRGMNW/pxGYLWvao3soOJMtp6cQ5KIYGuX92DMon/UgPBqEygeUj7aIqjhRss0
b0dUZQyHccAG+e5NeTF2ifHCEh2rZY18VGxPL7KLrCQigu5lif1TTv5CDO5rKrHl
TuTOsnooMxUH4ThIVI9cxXk6bzRMehLghA==
-----END CERTIFICATE-----

View File

@@ -1,20 +0,0 @@
-----BEGIN CERTIFICATE-----
MIIDRDCCAiwCCQDCm/Zrmali9zANBgkqhkiG9w0BAQUFADBkMQswCQYDVQQGEwJB
VTETMBEGA1UECBMKU29tZS1TdGF0ZTELMAkGA1UEBxMCSEgxDDAKBgNVBAoTA0ky
UDEPMA0GA1UECxMGcmVzZWVkMRQwEgYDVQQDEwtyZXNlZWQuaW5mbzAeFw0xMjEw
MjcxODU3NDNaFw0xNjEyMDUxODU3NDNaMGQxCzAJBgNVBAYTAkFVMRMwEQYDVQQI
EwpTb21lLVN0YXRlMQswCQYDVQQHEwJISDEMMAoGA1UEChMDSTJQMQ8wDQYDVQQL
EwZyZXNlZWQxFDASBgNVBAMTC3Jlc2VlZC5pbmZvMIIBIjANBgkqhkiG9w0BAQEF
AAOCAQ8AMIIBCgKCAQEAt9nz0iUvjdX4Hkhfk0FbBOeEP4i/FG3V4VrEdQfcviSF
XgzGYeRtGsvrFWP/5+6bcGnOkIy/jrKJfij3AjKJh8gTzqiNNNnV8VcHwFSNp+hZ
D4BM+UHPACV1Pjd3HQe6f0+LvcTs3HQgIkNkwUyqRuXOm/5Mk6SWSu1740aSwHCj
Kk0x1FByzI0YBvXCPX6TVk6sJqKkQyLzK0CSGSeqUq8GvGCq+jT9k62Su7ooxCwi
GzxaFjMdVYxuI8cuT5Cni+SUw1Ia8vhESnIy6slwzk37xNI80VuMvRT6rD2KcXDH
mK7ml1qL0rJWoF5AE+x/nen4V41mouv1W9rk3wTlTQIDAQABMA0GCSqGSIb3DQEB
BQUAA4IBAQAr6RBviBDW4bnPDTcdtstTDdaYX9yzoh+zzeGB0dUR26GKoOjpSItb
B9nrsW1eJ2wbblfGBUoXhcmNByKHXXHejMhmurHjdei2BuLbTsknN8DPKXu5UF9z
cg4cKQkxgzXOcNYlaF4+sfwFXDHJ4we/8vduVgkyo8R66543/Sh/nIMvq2slRT4w
wIBOVcMb2XxlbdwHW9XALAz9sto+4GH9GAC24f8ngluOpHijMnOOIo4dHibQ5hM9
KcDpHezP0ugMTAxS2NmtVahwAqa2IjpqR7aEQ2wLvxQzDqrXo93L93+b2FKRUQXH
Duud/n/w0kVV3DaIGikOsJayoanR+9HD
-----END CERTIFICATE-----

35
debian/copyright vendored
View File

@@ -1,15 +1,40 @@
Format: http://www.debian.org/doc/packaging-manuals/copyright-format/1.0/
Upstream-Name: i2pd
Source: https://github.com/PrivacySolutions
Source: https://github.com/PurpleI2P
Files: *
Copyright: 2013-2014 PrivacySolutions <press@privacysolutions.no>
License: GPL-2.0+
Copyright: 2013-2014 PurpleI2P
License: BSD-3-clause
Copyright (c) 2013-2015, The PurpleI2P Project
All rights reserved.
Redistribution and use in source and binary forms, with or without modification, are
permitted provided that the following conditions are met:
1. Redistributions of source code must retain the above copyright notice, this list of
conditions and the following disclaimer.
2. Redistributions in binary form must reproduce the above copyright notice, this list of
conditions and the following disclaimer in the documentation and/or other materials
provided with the distribution.
3. Neither the name of the copyright holder nor the names of its contributors may be used
to endorse or promote products derived from this software without specific prior written
permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY
EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR
TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
Files: debian/*
Copyright: 2014 hagen <hagen@i2pmail.org>
License: GPL-2.0+
License: GPL-2.0+
This package is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by

View File

@@ -3,22 +3,22 @@ COMMON_SRC = \
LeaseSet.cpp Log.cpp NTCPSession.cpp NetDb.cpp Reseed.cpp RouterContext.cpp \
RouterInfo.cpp SSU.cpp SSUSession.cpp SSUData.cpp Streaming.cpp Identity.cpp \
TransitTunnel.cpp Transports.cpp Tunnel.cpp TunnelEndpoint.cpp TunnelPool.cpp \
TunnelGateway.cpp Destination.cpp util.cpp aes.cpp base64.cpp
TunnelGateway.cpp Destination.cpp UPnP.cpp util.cpp aes.cpp base64.cpp
ifeq ($(UNAME),Darwin)
# This is needed on OS X for some reason I don't understand (yet).
# Else will get linker error about unknown symbols. - torkel
COMMON_SRC += \
BOB.cpp ClientContext.cpp Daemon.cpp I2PTunnel.cpp SAM.cpp SOCKS.cpp \
UPnP.cpp HTTPServer.cpp HTTPProxy.cpp i2p.cpp DaemonLinux.cpp
BOB.cpp ClientContext.cpp Daemon.cpp I2PTunnel.cpp I2PService.cpp SAM.cpp SOCKS.cpp \
UPnP.cpp HTTPServer.cpp HTTPProxy.cpp i2p.cpp DaemonLinux.cpp I2PControl.cpp
endif
# also: Daemon{Linux,Win32}.cpp will be added later
DAEMON_SRC = $(COMMON_SRC) \
BOB.cpp ClientContext.cpp Daemon.cpp I2PTunnel.cpp SAM.cpp SOCKS.cpp UPnP.cpp \
HTTPServer.cpp HTTPProxy.cpp i2p.cpp
BOB.cpp ClientContext.cpp Daemon.cpp I2PTunnel.cpp I2PService.cpp SAM.cpp SOCKS.cpp \
HTTPServer.cpp HTTPProxy.cpp I2PControl.cpp i2p.cpp
LIB_SRC := $(COMMON_SRC) \
api.cpp

View File

@@ -1,3 +1,4 @@
#include <cstdlib>
#include <string>
#include <algorithm>
#include <cctype>
@@ -241,40 +242,8 @@ namespace http
// User-Agent is needed to get the server list routerInfo files.
site << "GET " << u.path_ << " HTTP/1.1\r\nHost: " << u.host_
<< "\r\nAccept: */*\r\n" << "User-Agent: Wget/1.11.4\r\n" << "Connection: close\r\n\r\n";
// read response
std::string version, statusMessage;
site >> version; // HTTP version
int status;
site >> status; // status
std::getline (site, statusMessage);
if (status == 200) // OK
{
bool isChunked = false;
std::string header;
while (!site.eof () && header != "\r")
{
std::getline(site, header);
auto colon = header.find (':');
if (colon != std::string::npos)
{
std::string field = header.substr (0, colon);
if (field == i2p::util::http::TRANSFER_ENCODING)
isChunked = (header.find ("chunked", colon + 1) != std::string::npos);
}
}
std::stringstream ss;
if (isChunked)
MergeChunkedResponse (site, ss);
else
ss << site.rdbuf();
return ss.str();
}
else
{
LogPrint ("HTTP response ", status);
return "";
}
// read response and extract content
return GetHttpContent (site);
}
else
{
@@ -289,6 +258,43 @@ namespace http
}
}
std::string GetHttpContent (std::istream& response)
{
std::string version, statusMessage;
response >> version; // HTTP version
int status;
response >> status; // status
std::getline (response, statusMessage);
if (status == 200) // OK
{
bool isChunked = false;
std::string header;
while (!response.eof () && header != "\r")
{
std::getline(response, header);
auto colon = header.find (':');
if (colon != std::string::npos)
{
std::string field = header.substr (0, colon);
if (field == i2p::util::http::TRANSFER_ENCODING)
isChunked = (header.find ("chunked", colon + 1) != std::string::npos);
}
}
std::stringstream ss;
if (isChunked)
MergeChunkedResponse (response, ss);
else
ss << response.rdbuf();
return ss.str();
}
else
{
LogPrint ("HTTP response ", status);
return "";
}
}
void MergeChunkedResponse (std::istream& response, std::ostream& merged)
{
while (!response.eof ())
@@ -444,6 +450,16 @@ namespace http
query_.assign(query_i, url_s.end());
}
std::string urlDecode(const std::string& data)
{
std::string res(data);
for (size_t pos = res.find('%'); pos != std::string::npos; pos = res.find('%',pos+1))
{
char c = strtol(res.substr(pos+1,2).c_str(), NULL, 16);
res.replace(pos,3,1,c);
}
return res;
}
}
namespace net

2
util.h
View File

@@ -47,8 +47,10 @@ namespace util
const char TRANSFER_ENCODING[] = "Transfer-Encoding";
std::string httpRequest(const std::string& address);
std::string GetHttpContent (std::istream& response);
void MergeChunkedResponse (std::istream& response, std::ostream& merged);
int httpRequestViaI2pProxy(const std::string& address, std::string &content); // return http code
std::string urlDecode(const std::string& data);
struct url {
url(const std::string& url_s); // omitted copy, ==, accessors, ...

View File

@@ -2,7 +2,7 @@
#define _VERSION_H_
#define CODENAME "Purple"
#define VERSION "0.6.0"
#define I2P_VERSION "0.9.17"
#define VERSION "0.8.0"
#define I2P_VERSION "0.9.18"
#endif