@@ -5,18 +5,20 @@ mod services;
5
5
mod structures;
6
6
7
7
use std:: {
8
- net:: { Ipv4Addr , SocketAddr } ,
8
+ net:: { IpAddr , Ipv4Addr , SocketAddr } ,
9
+ str:: FromStr ,
9
10
sync:: { Arc , PoisonError , RwLock } ,
10
11
time:: Duration ,
11
12
} ;
12
13
13
14
use askama:: Template ;
14
15
use axum:: {
15
16
body:: Body ,
16
- extract:: { Path , Query , Request , State } ,
17
+ extract:: { FromRequestParts , Path , Query , Request , State } ,
17
18
handler:: Handler ,
18
19
http:: {
19
20
header:: { ACCEPT , CACHE_CONTROL , CONTENT_TYPE } ,
21
+ request:: Parts ,
20
22
HeaderName , HeaderValue , StatusCode ,
21
23
} ,
22
24
middleware:: Next ,
@@ -302,7 +304,9 @@ async fn ping_frame(
302
304
State ( state) : State < AppState > ,
303
305
CspNonce ( nonce) : CspNonce ,
304
306
Path ( ( edition, hostname) ) : Path < ( String , String ) > ,
307
+ CfConnectingIp ( ip) : CfConnectingIp ,
305
308
) -> Result < PingFrameTemplate , Failure > {
309
+ info ! ( edition, path = "frame" , target = hostname, on_behalf = ?ip, "Pinging server" ) ;
306
310
let ping = ping_generic ( & edition, hostname. clone ( ) ) . await ?;
307
311
Ok ( PingFrameTemplate {
308
312
ping,
@@ -327,7 +331,9 @@ pub struct PingElementTemplate {
327
331
async fn ping_markup (
328
332
State ( state) : State < AppState > ,
329
333
Path ( ( edition, hostname) ) : Path < ( String , String ) > ,
334
+ CfConnectingIp ( ip) : CfConnectingIp ,
330
335
) -> Result < PingElementTemplate , Failure > {
336
+ info ! ( edition, path = "markup" , target = hostname, on_behalf = ?ip, "Pinging server" ) ;
331
337
let ping = ping_generic ( & edition, hostname. clone ( ) ) . await ?;
332
338
Ok ( PingElementTemplate {
333
339
ping,
@@ -338,9 +344,12 @@ async fn ping_markup(
338
344
} )
339
345
}
340
346
341
- async fn ping_image ( Path ( ( edition, hostname) ) : Path < ( String , String ) > ) -> Result < Png , StatusCode > {
347
+ async fn ping_image (
348
+ Path ( ( edition, hostname) ) : Path < ( String , String ) > ,
349
+ CfConnectingIp ( ip) : CfConnectingIp ,
350
+ ) -> Result < Png , StatusCode > {
342
351
const PREFIX_LEN : usize = "data:image/png;base64," . len ( ) ;
343
- debug ! ( edition, hostname, "Serving icon ") ;
352
+ info ! ( edition, path = "image" , target = hostname, on_behalf = ?ip , "Pinging server ") ;
344
353
let ping = match ping_generic ( & edition, hostname. clone ( ) ) . await {
345
354
Ok ( v) => v,
346
355
Err ( e) => {
@@ -362,11 +371,19 @@ async fn ping_image(Path((edition, hostname)): Path<(String, String)>) -> Result
362
371
Ok ( Png ( decoded) )
363
372
}
364
373
365
- async fn handle_java_ping ( Path ( address) : Path < String > ) -> Result < Json < MCPingResponse > , Failure > {
374
+ async fn handle_java_ping (
375
+ Path ( address) : Path < String > ,
376
+ CfConnectingIp ( ip) : CfConnectingIp ,
377
+ ) -> Result < Json < MCPingResponse > , Failure > {
378
+ info ! ( edition = "java" , path = "api" , target = address, on_behalf = ?ip, "Pinging server" ) ;
366
379
Ok ( Json ( ping_java ( address) . await ?) )
367
380
}
368
381
369
- async fn handle_bedrock_ping ( Path ( address) : Path < String > ) -> Result < Json < MCPingResponse > , Failure > {
382
+ async fn handle_bedrock_ping (
383
+ Path ( address) : Path < String > ,
384
+ CfConnectingIp ( ip) : CfConnectingIp ,
385
+ ) -> Result < Json < MCPingResponse > , Failure > {
386
+ info ! ( edition = "bedrock" , path = "api" , target = address, on_behalf = ?ip, "Pinging server" ) ;
370
387
Ok ( Json ( ping_bedrock ( address) . await ?) )
371
388
}
372
389
@@ -394,6 +411,10 @@ pub enum Failure {
394
411
StatusReqwestFailed ( #[ from] reqwest:: Error ) ,
395
412
#[ error( "JSON processing error" ) ]
396
413
JsonProcessingFailed ( #[ from] serde_json:: Error ) ,
414
+ #[ error( "Could not convert header to string" ) ]
415
+ HeaderToStr ( #[ from] axum:: http:: header:: ToStrError ) ,
416
+ #[ error( "Could not convert string to IP address" ) ]
417
+ AddressParse ( #[ from] std:: net:: AddrParseError ) ,
397
418
#[ error( "Status lock poisoned. Please try again in 5 minutes." ) ]
398
419
LockPoisoned ,
399
420
#[ error( "No server address specified!" ) ]
@@ -414,7 +435,10 @@ impl IntoResponse for Failure {
414
435
Self :: ConnectionFailed ( _) | Self :: TimedOut => StatusCode :: OK ,
415
436
Self :: StatusReqwestFailed ( _) => StatusCode :: BAD_GATEWAY ,
416
437
Self :: JsonProcessingFailed ( _) | Self :: LockPoisoned => StatusCode :: INTERNAL_SERVER_ERROR ,
417
- Self :: NoHostname | Self :: UnknownEdition => StatusCode :: BAD_REQUEST ,
438
+ Self :: NoHostname
439
+ | Self :: UnknownEdition
440
+ | Self :: AddressParse ( _)
441
+ | Self :: HeaderToStr ( _) => StatusCode :: BAD_REQUEST ,
418
442
} ;
419
443
error ! ( error = ?self , "Error processing request" ) ;
420
444
( status, Extension ( Arc :: new ( self ) ) , Body :: empty ( ) ) . into_response ( )
@@ -503,3 +527,23 @@ impl IntoResponse for Png {
503
527
( headers, self . 0 ) . into_response ( )
504
528
}
505
529
}
530
+
531
+ pub struct CfConnectingIp ( pub IpAddr ) ;
532
+
533
+ #[ axum:: async_trait]
534
+ impl < S > FromRequestParts < S > for CfConnectingIp {
535
+ type Rejection = Failure ;
536
+
537
+ async fn from_request_parts ( parts : & mut Parts , _: & S ) -> Result < Self , Self :: Rejection > {
538
+ static NAME : HeaderName = HeaderName :: from_static ( "cf-connecting-ip" ) ;
539
+
540
+ let Some ( ip_hdr) = parts. headers . get ( & NAME ) else {
541
+ warn ! ( "Cloudflare did not send cf-connecting-ip" ) ;
542
+ return Ok ( Self ( IpAddr :: V4 ( Ipv4Addr :: UNSPECIFIED ) ) ) ;
543
+ } ;
544
+
545
+ let ip_str = ip_hdr. to_str ( ) ?;
546
+ let ip = IpAddr :: from_str ( ip_str) ?;
547
+ Ok ( Self ( ip) )
548
+ }
549
+ }
0 commit comments