() * (reconnects as f32) * 1000.0) as u64;
96 |
97 | let mut outbound = match TcpStream::connect(proxy_addr).await {
98 | Ok(o) => o,
99 | Err(_) => return Ok(()),
100 | };
101 | let (mut read_inbound, mut write_inbound) = inbound.split();
102 | let (read_outbound, mut write_outbound) = outbound.split();
103 |
104 | let client_to_server = async {
105 | // uses read_inbound and write_outbound
106 |
107 | // println!("start");
108 | // wait lag before actually letting them connect
109 | tokio::time::sleep(StdDuration::from_millis(simulated_ping)).await;
110 |
111 | for packet in get_all_packets(&mut read_inbound, &mut write_outbound).await {
112 | let logger = logger.clone();
113 | let ip = split[0].to_string();
114 | let p = packet.clone();
115 | tokio::spawn(async move { logger.write().await.handle_connect(p, &ip).await });
116 | }
117 |
118 | // copy packets from ri to wo
119 | let packet_queue: VecDeque<(BytesMut, tokio::time::Instant)> = VecDeque::new();
120 | let packet_queue = Arc::new(std::sync::Mutex::new(packet_queue));
121 |
122 | let task_packet_queue = packet_queue.clone();
123 |
124 | // read from the queue and write to write_outbound
125 | let read_from_queue = async move {
126 | loop {
127 | // check if there's something in the packet queue every simulated_ping ms
128 | tokio::time::sleep(StdDuration::from_millis(simulated_ping)).await;
129 | // if there is, wait until the packet is ready to be sent and send it
130 | loop {
131 | let queue_front = packet_queue.lock().unwrap().pop_front();
132 | if let Some((bytes, sent_at)) = &queue_front {
133 | let sending_at = *sent_at + StdDuration::from_millis(simulated_ping);
134 | tokio::time::sleep_until(sending_at).await;
135 | if write_outbound.write_all(bytes.as_ref()).await.is_err() {
136 | break;
137 | }
138 | } else {
139 | break;
140 | }
141 | }
142 | }
143 | };
144 |
145 | let write_to_queue = async move {
146 | let mut framed = FramedRead::new(read_inbound, BytesCodec::new());
147 | while let Some(message) = framed.next().await {
148 | match message {
149 | Ok(bytes) => {
150 | task_packet_queue
151 | .lock()
152 | .unwrap()
153 | .push_back((bytes, tokio::time::Instant::now()));
154 | }
155 | Err(_) => break,
156 | }
157 | }
158 | };
159 |
160 | tokio::join!(read_from_queue, write_to_queue);
161 | };
162 |
163 | let server_to_client = async {
164 | let mut outbound_framed = FramedRead::new(read_outbound, BytesCodec::new());
165 | // copy packets from ro to wi
166 | while let Some(message) = outbound_framed.next().await {
167 | match message {
168 | Ok(bytes) => {
169 | if write_inbound.write_all(&bytes).await.is_err() {
170 | break;
171 | }
172 | }
173 | Err(_) => break,
174 | }
175 | }
176 |
177 | write_inbound.shutdown().await
178 | };
179 |
180 | let _ = tokio::try_join!(
181 | tokio::time::timeout(StdDuration::from_secs(timeout_seconds), client_to_server),
182 | tokio::time::timeout(StdDuration::from_secs(timeout_seconds), server_to_client)
183 | );
184 |
185 | // so it times out
186 | tokio::time::sleep(StdDuration::from_millis(5 * 60 * 1000)).await;
187 |
188 | Ok(())
189 | }
190 |
--------------------------------------------------------------------------------
/src/packet.rs:
--------------------------------------------------------------------------------
1 | // minecraft honeypot does honeypot things for minecraft and proxies which is cool
2 | // Copyright (C) 2022 cleonyc
3 |
4 | // This program is free software: you can redistribute it and/or modify
5 | // it under the terms of the GNU General Public License as published by
6 | // the Free Software Foundation, either version 3 of the License, or
7 | // (at your option) any later version.
8 |
9 | // This program is distributed in the hope that it will be useful,
10 | // but WITHOUT ANY WARRANTY; without even the implied warranty of
11 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 | // GNU General Public License for more details.
13 |
14 | // You should have received a copy of the GNU General Public License
15 | // along with this program. If not, see .
16 |
17 | use azalea_protocol::packets::handshake::ServerboundHandshakePacket;
18 | use azalea_protocol::packets::login::ServerboundLoginPacket;
19 | use azalea_protocol::read::read_packet;
20 | use tokio::io::{AsyncRead, AsyncReadExt, AsyncWrite, AsyncWriteExt};
21 |
22 | // pub async fn read_packet<'a, P: ProtocolPacket, R>(
23 | // stream: &'a mut R,
24 | // ) -> anyhow::Result<(Option, Vec)>
25 | // where
26 | // R: AsyncRead + std::marker::Unpin + std::marker::Send + std::marker::Sync,
27 | // {
28 | // // let start_time = std::time::Instant::now();
29 |
30 | // // println!("decrypting packet ({}ms)", start_time.elapsed().as_millis());
31 | // // if we were given a cipher, decrypt the packet
32 | // let buf = frame_splitter(stream).await.unwrap();
33 | // println!("24");
34 | // // println!("splitting packet ({}ms)", start_time.elapsed().as_millis());
35 | // if buf.1.len() == 0 {
36 | // return Ok((None, buf.1));
37 | // }
38 | // let mut orig = buf.1.clone();
39 | // println!("31");
40 | // // println!("decoding packet ({}ms)", start_time.elapsed().as_millis());
41 | // let packet: (Option, Vec) = packet_decoder(&mut buf.0.as_slice()).await?;
42 | // println!("34");
43 | // // println!("decoded packet ({}ms)", start_time.elapsed().as_millis());
44 | // // orig.push(packet.1);
45 | // Ok((packet.0, orig))
46 | // }
47 |
48 | async fn frame_splitter(mut stream: &mut R) -> anyhow::Result<(Vec, Vec)>
49 | where
50 | R: AsyncRead + std::marker::Unpin + std::marker::Send,
51 | {
52 | // Packet Length
53 | // println!("called");
54 | let res = match read_varint_async(&mut stream).await {
55 | Ok(len) => len,
56 | Err(_) => {
57 | // println!("err reading varint");
58 | return Ok((vec![], vec![]));
59 | }
60 | };
61 | // println!("fs varint read: {}", res.0);
62 |
63 | if res.0 > 1024 || res.0 == 0 {
64 | return Ok((vec![], res.1));
65 | }
66 | let mut read = vec![];
67 | let length = res.0;
68 | while read.len() < length.try_into().unwrap() {
69 | let mut buf = [0; 1];
70 | match stream.read_exact(&mut buf).await {
71 | Ok(_) => read.push(buf[0]),
72 | Err(_) => {
73 | break;
74 | }
75 | };
76 | }
77 |
78 | let mut orig = res.1;
79 | // println!("74: orig starts {:?}, appending: {:?}", orig, read);
80 |
81 | orig.append(&mut (read.clone()));
82 | let (valid_packet, _) = safe_check_packet_id(&mut read.as_slice()).await;
83 | if !valid_packet {
84 | return Ok((vec![], orig));
85 | }
86 | Ok((read, orig))
87 | }
88 |
89 | // async fn packet_decoder(
90 | // mut stream: &mut R,
91 | // ) -> anyhow::Result<(Option, Vec)>
92 | // where
93 | // R: AsyncRead + std::marker::Unpin + std::marker::Send + std::io::Read,
94 | // {
95 | // // Packet ID
96 | // let packet_id = read_varint_async(&mut stream).await?;
97 | // if packet_id.0 != 0x00 {
98 | // return Ok((None, packet_id.1));
99 | // }
100 | // let read = P::read(packet_id.0.try_into().unwrap(), stream)?;
101 |
102 | // Ok((Some(read), vec![]))
103 | // }
104 | // fast varints modified from https://github.com/luojia65/mc-varint/blob/master/src/lib.rs#L67
105 | /// Read a single varint from the reader and return the value, along with the number of bytes read
106 | pub async fn read_varint_async(
107 | reader: &mut (dyn AsyncRead + Unpin + Send),
108 | ) -> anyhow::Result<(i32, Vec)> {
109 | let mut buffer = [0];
110 | let mut orig = vec![];
111 | let mut ans = 0;
112 | for i in 0..5 {
113 | if let Ok(n) = reader.read(&mut buffer).await && n > 0 {
114 | ans |= ((buffer[0] & 0b0111_1111) as i32) << (7 * i);
115 | orig.push(buffer[0]);
116 | if buffer[0] & 0b1000_0000 == 0 {
117 | return Ok((ans, orig));
118 | }
119 | };
120 | }
121 | Ok((ans, orig))
122 | }
123 | pub async fn safe_check_packet_id(reader: &mut (dyn AsyncRead + Unpin + Send)) -> (bool, Vec) {
124 | let packet_id = match read_varint_async(reader).await {
125 | Ok(packet_id) => packet_id,
126 | Err(_) => return (false, vec![]),
127 | };
128 | if packet_id.0 != 0x00 {
129 | return (false, packet_id.1);
130 | };
131 | (true, packet_id.1)
132 | }
133 | #[derive(Clone, Debug)]
134 | pub enum PossiblePacket {
135 | LoginStart { packet: ServerboundLoginPacket },
136 | Status { packet: ServerboundHandshakePacket },
137 | }
138 | pub async fn try_get_packet(
139 | stream: &mut R,
140 | writer: &mut W,
141 | ) -> Option
142 | where
143 | R: AsyncRead + std::marker::Unpin + std::marker::Send,
144 | W: AsyncWrite + std::marker::Unpin + std::marker::Send,
145 | {
146 | let (read_bytes, original_bytes) = frame_splitter(stream).await.unwrap();
147 | // println!("138: orig from frame_splitter: {:?}", original_bytes);
148 | // 1024 bytes *should* be the theoretical maximum for login or status packets that we care about
149 | // might break if it's an actual valid user key, we'll see
150 | if original_bytes.len() > 20000 || original_bytes.is_empty() || read_bytes.is_empty() {
151 | // println!("bad byte len");
152 | writer.write_all(&original_bytes).await.unwrap();
153 | // io::copy(stream, writer).await.unwrap();
154 | return None;
155 | }
156 | if let Ok(packet) = read_packet::(
157 | &mut original_bytes.clone().as_slice(),
158 | None,
159 | &mut None,
160 | )
161 | .await
162 | {
163 | writer.write_all(&original_bytes).await.unwrap();
164 | // io::copy(stream, writer).await.unwrap();
165 | // println!("returning! <3");
166 | return Some(PossiblePacket::Status { packet });
167 | };
168 | if let Ok(packet) = read_packet::(
169 | &mut adapt_from_1_18(&original_bytes.clone()).as_slice(),
170 | None,
171 | &mut None,
172 | )
173 | .await
174 | {
175 | // println!("\n\n\n\n\n\n\n\n\nreturning! (login) <3");
176 | writer.write_all(&original_bytes).await.unwrap();
177 | // io::copy(stream, writer).await.unwrap();
178 | // println!("returning! L<3");
179 | return Some(PossiblePacket::LoginStart { packet });
180 | }
181 |
182 | // println!("returning! :(");
183 | writer.write_all(&original_bytes).await.unwrap();
184 | // io::copy(stream, writer).await.unwrap();
185 | None
186 | }
187 | pub async fn get_all_packets(
188 | stream: &mut R,
189 | writer: &mut W,
190 | ) -> Vec
191 | where
192 | R: AsyncRead + std::marker::Unpin + std::marker::Send,
193 | W: AsyncWrite + std::marker::Unpin + std::marker::Send,
194 | {
195 | let mut ret = vec![];
196 | while let Some(packet) = try_get_packet(stream, writer).await {
197 | ret.push(packet);
198 | }
199 | ret
200 | }
201 |
202 | fn adapt_from_1_18(bytes: &[u8]) -> Vec {
203 | let mut clone = Vec::with_capacity(bytes.len() + 2);
204 | clone.extend_from_slice(bytes);
205 | clone[0] += 2;
206 | clone.extend_from_slice(&[0x00, 0x00]);
207 | clone
208 | }
209 |
210 | #[cfg(test)]
211 | mod tests {
212 | use azalea_protocol::{
213 | packets::handshake::client_intention_packet::ClientIntentionPacket, write::write_packet,
214 | };
215 | use rand::Rng;
216 | use tokio::io::AsyncWriteExt;
217 |
218 | use crate::packet::{adapt_from_1_18, get_all_packets, try_get_packet};
219 |
220 | #[tokio::test]
221 | async fn test_adapt_1_18() {
222 | assert_eq!(
223 | adapt_from_1_18(&[6, 0, 4, 50, 57, 55, 56]),
224 | vec![8, 0, 4, 50, 57, 55, 56, 0, 0]
225 | );
226 | }
227 | #[tokio::test]
228 | async fn check_packets() {
229 | // let hello_packet = ServerboundHelloPacket {
230 | // username: "2978".to_string(),
231 | // public_key: None,
232 | // profile_id: None,
233 | // }
234 | // .get();
235 | let mut random_data: [u8; 2048] = [0; 2048];
236 | rand::thread_rng().fill(&mut random_data);
237 | let random_data_orig = random_data;
238 | {
239 | let mut buf = vec![];
240 |
241 | write_packet(
242 | ClientIntentionPacket {
243 | protocol_version: 758,
244 | hostname: "localhost".to_string(),
245 | port: 25565,
246 | intention: azalea_protocol::packets::ConnectionProtocol::Login,
247 | }
248 | .get(),
249 | &mut buf,
250 | None,
251 | &mut None,
252 | )
253 | .await
254 | .unwrap();
255 | // old 1.18.2 hello packet
256 | buf.write_all(&[6, 0, 4, 50, 57, 55, 56]).await.unwrap();
257 |
258 | // write_packet(hello_packet, &mut buf, None, &mut None)
259 | // .await
260 | // .unwrap();
261 | let mut orig_data = vec![];
262 | let packets = get_all_packets(&mut buf.as_slice(), &mut orig_data).await;
263 | assert_eq!(packets.len(), 2);
264 | println!("packets: {:?}", packets);
265 | assert_eq!(buf, orig_data);
266 | }
267 | {
268 | let mut modded_rand_data: &[u8] = &random_data;
269 | let mut orig_data = vec![];
270 | let packet = try_get_packet(&mut modded_rand_data, &mut orig_data).await;
271 | assert!(packet.is_none());
272 | assert_eq!(orig_data, random_data_orig);
273 | }
274 | }
275 | }
276 |
277 | // fn combine(first: &Vec, second: &Vec) -> Vec {
278 | // // println!("combining {:?} with {:?}", first, second);
279 | // let mut new = vec![];
280 | // new.append(&mut first.clone());
281 | // new.append(&mut second.clone());
282 | // new
283 | // }
284 |
--------------------------------------------------------------------------------
/src/webhook.rs:
--------------------------------------------------------------------------------
1 | // minecraft honeypot does honeypot things for minecraft and proxies which is cool
2 | // Copyright (C) 2022 cleonyc
3 |
4 | // This program is free software: you can redistribute it and/or modify
5 | // it under the terms of the GNU General Public License as published by
6 | // the Free Software Foundation, either version 3 of the License, or
7 | // (at your option) any later version.
8 |
9 | // This program is distributed in the hope that it will be useful,
10 | // but WITHOUT ANY WARRANTY; without even the implied warranty of
11 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 | // GNU General Public License for more details.
13 |
14 | // You should have received a copy of the GNU General Public License
15 | // along with this program. If not, see .
16 | use isahc::{AsyncReadResponseExt, HttpClient, Request};
17 | use serde_json::Value;
18 | use webhook::{client::WebhookClient, models::Message};
19 |
20 | use crate::database::{Client, Database, Login, Ping};
21 |
22 | #[derive(Clone)]
23 | pub struct SummaryWebhook {
24 | url: String,
25 | pub message_ids: Vec,
26 | client: HttpClient,
27 | }
28 | impl SummaryWebhook {
29 | pub async fn new(
30 | url: String,
31 | message_ids: Vec,
32 | database: Database,
33 | ) -> anyhow::Result {
34 | let client = HttpClient::new()?;
35 | let mut ret = Self {
36 | url,
37 | message_ids,
38 | client,
39 | };
40 | ret.update(database.clone()).await?;
41 |
42 | Ok(ret)
43 | }
44 |
45 | pub async fn update(&mut self, database: Database) -> anyhow::Result<()> {
46 | let gen_messages = gen_summmary_messages(database.clone());
47 | if gen_messages.len() != self.message_ids.len() {
48 | for msg in gen_messages.iter().skip(self.message_ids.len()) {
49 | let req = Request::post(&format!("{}?wait=true", self.url.trim_end_matches('/')))
50 | .header("Content-Type", "application/json")
51 | .body(serde_json::to_string(&msg)?)?;
52 | let mut resp = self.client.send_async(req).await?;
53 | let json: Value = resp.json().await?;
54 | let id: u64 = json["id"]
55 | .as_str()
56 | .expect("bad id in response from discord when creating webhook, what the fuck")
57 | .parse()
58 | .expect("apparently the id from the response is not a number (WTF??)");
59 | self.message_ids.push(id);
60 | }
61 | }
62 | for (index, msg) in gen_summmary_messages(database.clone()).iter().enumerate() {
63 | self.client
64 | .send_async(
65 | Request::patch(&format!(
66 | "{}/messages/{}",
67 | self.url.clone(),
68 | self.message_ids[index]
69 | ))
70 | .header("Content-Type", "application/json")
71 | // this is the best way to handle rate limits:
72 | .header("x-pls-no-rate-limit", "owo")
73 | .body(serde_json::to_string(&msg)?)?,
74 | )
75 | .await?;
76 | }
77 | Ok(())
78 | }
79 | }
80 | fn gen_summmary_messages(database: Database) -> Vec {
81 | let mut ret = vec![];
82 | for (chunk_num, chunk) in database.data.chunks(25).enumerate() {
83 | let mut m = Message::new();
84 | m.embed(|e| {
85 | if chunk_num == 0 {
86 | e.title("Clients");
87 | }
88 | for client in chunk
89 | {
90 | e.field(
91 | &format!("`{}`", &client.ip),
92 | &format!(
93 | "Pings: `{}` ({}), Logins: `{}` ({}), `{}`",
94 | client.pings.len(),
95 | if client.pings.is_empty() {
96 | "N/A".to_string()
97 | } else {
98 | format!(
99 | "",
100 | client
101 | .pings
102 | .iter()
103 | .reduce(|a, b| if a.time > b.time { b } else { a })
104 | .unwrap()
105 | .time
106 | .unix_timestamp()
107 | )
108 | },
109 | client.logins.len(),
110 | if client.logins.is_empty() {
111 | "N/A".to_string()
112 | } else {
113 | format!(
114 | "",
115 | client
116 | .logins
117 | .iter()
118 | .reduce(|a, b| if a.time > b.time { b } else { a })
119 | .unwrap()
120 | .time
121 | .unix_timestamp()
122 | )
123 | },
124 | client.ipinfo
125 | ),
126 | false,
127 | );
128 | }
129 | e
130 | });
131 | ret.push(m)
132 | }
133 | ret
134 | }
135 | #[derive(Clone)]
136 | pub struct ConWebhook {
137 | url: String,
138 | }
139 | impl ConWebhook {
140 | pub fn new(url: String) -> Self {
141 | Self { url }
142 | }
143 | pub async fn handle_login(&self, client: Client, login: Login) -> anyhow::Result<()> {
144 | WebhookClient::new(&self.url)
145 | .send(|m| {
146 | m.content(
147 | format!(
148 | "`{}` joined the server
149 | {} | {}
150 | {}
151 | ",
152 | login.username,
153 | pretty_ip(&client.ip),
154 | client.ipinfo,
155 | if client.logins.len() == 1 {
156 | "**First Login**".to_string()
157 | } else {
158 | format!("Previous logins: `{}`", client.logins.len())
159 | }
160 | )
161 | .trim(),
162 | )
163 | })
164 | .await
165 | .expect("failed to send webhook");
166 | Ok(())
167 | }
168 | pub async fn handle_ping(&self, client: Client, ping: Ping) -> anyhow::Result<()> {
169 | WebhookClient::new(&self.url)
170 | .send(|m| {
171 | m.content(&format!(
172 | "Ping from {}, Con: {}, Target: {}",
173 | pretty_ip(&client.ip),
174 | client.ipinfo,
175 | ping.target
176 | ))
177 | })
178 | .await
179 | .unwrap();
180 | Ok(())
181 | }
182 | }
183 |
184 | fn pretty_ip(ip: &str) -> String {
185 | format!("[`{}`]()", ip, ip)
186 | }
187 |
--------------------------------------------------------------------------------