@@ -309,11 +309,6 @@ func (r *policyRuleSet) Allow(ctx context.Context, req *socks5.Request) (context
309309 // partial persistence failure does not silently allow all subsequent
310310 // requests without re-triggering per-request policy.
311311 var alwaysAllowPersisted bool
312- // askApprovedOnce is true when the connection-level ask resolved to
313- // ResponseAllowOnce. Used to seed the per-request checker with a single
314- // prepaid allow credit so the first HTTP request does not re-prompt the
315- // user (double-prompt fix).
316- var askApprovedOnce bool
317312 switch verdict {
318313 case policy .Allow :
319314 allowed = true
@@ -323,39 +318,15 @@ func (r *policyRuleSet) Allow(ctx context.Context, req *socks5.Request) (context
323318 reason = "ask treated as deny (no approval broker)"
324319 log .Printf ("[ASK->DENY] %s:%d (no approval broker)" , dest , port )
325320 } else {
326- log .Printf ("[ASK] %s:%d (waiting for Telegram approval)" , dest , port )
327- timeout := time .Duration (eng .TimeoutSec ) * time .Second
328- proto := DetectProtocol (port )
329- resp , err := r .broker .Request (dest , port , proto .String (), timeout )
330- if err != nil {
331- effectiveVerdict = policy .Deny
332- reason = fmt .Sprintf ("approval timeout: %v" , err )
333- log .Printf ("[ASK->DENY] %s:%d (timeout: %v)" , dest , port , err )
334- } else {
335- switch resp {
336- case channel .ResponseAllowOnce :
337- allowed = true
338- effectiveVerdict = policy .Allow
339- reason = "user approved once"
340- askApprovedOnce = true
341- log .Printf ("[ASK->ALLOW] %s:%d (user approved once)" , dest , port )
342- case channel .ResponseAlwaysAllow :
343- allowed = true
344- effectiveVerdict = policy .Allow
345- reason = "user approved always"
346- log .Printf ("[ASK->ALLOW+SAVE] %s:%d (user approved always)" , dest , port )
347- alwaysAllowPersisted = r .persistAlwaysAllow (dest , port )
348- case channel .ResponseAlwaysDeny :
349- effectiveVerdict = policy .Deny
350- reason = "user denied always"
351- log .Printf ("[ASK->DENY+SAVE] %s:%d (user denied always)" , dest , port )
352- r .persistAlwaysDeny (dest , port )
353- default :
354- effectiveVerdict = policy .Deny
355- reason = "user denied"
356- log .Printf ("[ASK->DENY] %s:%d (user denied)" , dest , port )
357- }
358- }
321+ // Auto-allow the SOCKS5 CONNECT without prompting the user.
322+ // Approval happens per-request inside the MITM handler where
323+ // the HTTP method and path are known, producing a single
324+ // combined Telegram message per request instead of two
325+ // separate messages (connection + request).
326+ allowed = true
327+ effectiveVerdict = policy .Allow
328+ reason = "ask deferred to per-request"
329+ log .Printf ("[ASK->DEFER] %s:%d (approval deferred to per-request)" , dest , port )
359330 }
360331 }
361332
@@ -444,13 +415,11 @@ func (r *policyRuleSet) Allow(ctx context.Context, req *socks5.Request) (context
444415 if skipPerRequest {
445416 ctx = context .WithValue (ctx , ctxKeySkipPerRequest , true )
446417 } else {
447- seed := 0
448- if askApprovedOnce {
449- seed = 1
450- }
418+ // No seed credit: every HTTP request triggers its own
419+ // per-request approval with method and path visible in
420+ // the Telegram message.
451421 checker := NewRequestPolicyChecker (r .engine , r .broker ,
452422 WithPersist (r .buildPersistFunc ()),
453- WithSeedCredits (seed ),
454423 )
455424 ctx = context .WithValue (ctx , ctxKeyPerRequestPolicy , checker )
456425 }
@@ -1447,56 +1416,14 @@ func (s *Server) sniPolicyCheckBeforeDial(ctx context.Context, request *socks5.R
14471416 log .Printf ("[SNI->DENY] %s:%d (hostname %s: ask treated as deny, no broker)" , ipStr , port , sni )
14481417 return nil , ctx , false
14491418 }
1450- log .Printf ("[SNI->ASK] %s:%d (hostname %s: waiting for approval)" , ipStr , port , sni )
1451- timeout := time .Duration (eng .TimeoutSec ) * time .Second
1452- proto := DetectProtocol (port )
1453- resp , reqErr := s .rules .broker .Request (sni , port , proto .String (), timeout )
1454- if reqErr != nil {
1455- log .Printf ("[SNI->DENY] %s:%d (hostname %s: approval timeout: %v)" , ipStr , port , sni , reqErr )
1456- return nil , ctx , false
1457- }
1458- switch resp {
1459- case channel .ResponseAllowOnce :
1460- log .Printf ("[SNI->ALLOW] %s:%d (hostname %s: user approved once)" , ipStr , port , sni )
1461- // Ask-approved once: attach a checker seeded with one prepaid
1462- // allow credit so the first HTTP request on the new tunnel does
1463- // not re-prompt the user (the SNI-level approval is reused for
1464- // the first request). Subsequent requests on the same keep-alive
1465- // connection re-trigger the ask flow normally.
1466- checker := NewRequestPolicyChecker (s .rules .engine , s .rules .broker ,
1467- WithPersist (s .rules .buildPersistFunc ()),
1468- WithSeedCredits (1 ),
1469- )
1470- ctx = context .WithValue (ctx , ctxKeyPerRequestPolicy , checker )
1471- return reader , ctx , true
1472- case channel .ResponseAlwaysAllow :
1473- log .Printf ("[SNI->ALLOW+SAVE] %s:%d (hostname %s: user approved always)" , ipStr , port , sni )
1474- // Persist the new allow rule. If the persist path fails we
1475- // fall back to attaching a checker as a safety net so the
1476- // current keep-alive connection is still policy-checked on
1477- // every request (the failure was only logged inside
1478- // sniSaveRule, so the in-memory engine may still evaluate
1479- // sni:port as ask). The checker is seeded with one credit
1480- // because the user already approved this connection.
1481- if ok := s .sniSaveRule ("allow" , sni , port ); ok {
1482- ctx = context .WithValue (ctx , ctxKeySkipPerRequest , true )
1483- } else {
1484- log .Printf ("[SNI->ALLOW+SAVE] %s:%d persistence failed; attaching per-request checker as safety net" , sni , port )
1485- checker := NewRequestPolicyChecker (s .rules .engine , s .rules .broker ,
1486- WithPersist (s .rules .buildPersistFunc ()),
1487- WithSeedCredits (1 ),
1488- )
1489- ctx = context .WithValue (ctx , ctxKeyPerRequestPolicy , checker )
1490- }
1491- return reader , ctx , true
1492- case channel .ResponseAlwaysDeny :
1493- log .Printf ("[SNI->DENY+SAVE] %s:%d (hostname %s: user denied always)" , ipStr , port , sni )
1494- s .sniSaveRule ("deny" , sni , port )
1495- return nil , ctx , false
1496- default :
1497- log .Printf ("[SNI->DENY] %s:%d (hostname %s: user denied)" , ipStr , port , sni )
1498- return nil , ctx , false
1499- }
1419+ // Auto-allow the connection and defer approval to per-request
1420+ // checks where the HTTP method and path are visible.
1421+ log .Printf ("[SNI->DEFER] %s:%d (hostname %s: approval deferred to per-request)" , ipStr , port , sni )
1422+ checker := NewRequestPolicyChecker (s .rules .engine , s .rules .broker ,
1423+ WithPersist (s .rules .buildPersistFunc ()),
1424+ )
1425+ ctx = context .WithValue (ctx , ctxKeyPerRequestPolicy , checker )
1426+ return reader , ctx , true
15001427 default :
15011428 log .Printf ("[SNI->DENY] %s:%d (hostname %s: default deny)" , ipStr , port , sni )
15021429 return nil , ctx , false
0 commit comments