apernet / OpenGFW

OpenGFW is a flexible, easy-to-use, open source implementation of GFW (Great Firewall of China) on Linux
https://gfw.dev/
Mozilla Public License 2.0
9.47k stars 711 forks source link

[Bug] TCP Stream would not be blocked if domain name has multiple IP address #36

Closed KujouRinka closed 7 months ago

KujouRinka commented 7 months ago

My Enviroment

Config File

# block bilibili
- name: block bilibili http
  action: block
  expr: string(http?.req?.headers?.host) endsWith "bilibili.com"

- name: block bilibili https
  action: block
  expr: string(tls?.req?.sni) endsWith "bilibili.com"

# block csdn
- name: block csdn http
  action: block
  expr: string(http?.req?.headers?.host) endsWith "csdn.net"

- name: block csdn https
  action: block
  expr: string(tls?.req?.sni) endsWith "csdn.net"

What happeded

https://www.csdn.net has been blocked properly, while https://www.bilibili.com cannot.

Concretely, when visiting bilibili through https://www.bilibili.com in Chrome, the page is not accessible. But while web browser keeping trying to reconnect automatically, there's high possibility that the page will be loaded successfully. And after that, due to http long connection has benn established, all the followed-up operations will not be interrupted.

I found this happened on my:

Moreover, this problem only happened when domain name holding IP addresses more than one. For example, I query DNS record for bilibili.com and csdn.net:

$ nslookup bilibili.com
Server:         210.31.0.9
Address:        210.31.0.9#53

Non-authoritative answer:
Name:   bilibili.com
Address: 119.3.70.188
Name:   bilibili.com
Address: 8.134.50.24
Name:   bilibili.com
Address: 139.159.241.37
Name:   bilibili.com
Address: 47.103.24.173

$ nslookup csdn.net
Server:         210.31.0.9
Address:        210.31.0.9#53

Non-authoritative answer:
Name:   csdn.net
Address: 120.46.76.152

The result is that csdn can be blocked properly, while former cannot. I am not sure whether there is some relation between this bug and DNS record. But I think it's worth to mention.

tobyxdd commented 7 months ago

That's weird, I'll test it tomorrow. I don't think it has anything to do with how many IPs it has in DNS though, as the blocking is done at the HTTP/HTTPS protocol level, not DNS.

Do you know what protocol Chrome uses when it becomes accessible? Is it possible that it uses QUIC (which we don't support at the moment)?

tobyxdd commented 7 months ago

I saw that you mentioned this also happens with curl. Can you provide some info on how to reproduce this with curl?

KujouRinka commented 7 months ago

Run curl 'https://www.bilibili.com', and wait a while, we can see some response on terminal.

I try to wireshark it, attched file shows all packet it captured during this command runing and show response.

bili_cap.tar.gz

I use this to filter some packet: !((ip.src == 172.16.0.0/12 || ip.src == 10.0.0.0/8 || ip.src == 192.168.0.0/16) && (ip.dst == 192.168.0.0/16 || ip.dst == 172.16.0.0/12 || ip.dst == 10.0.0.0/8))

I forgot to attach my config.yaml:

io:
  queueSize: 1024
  local: true

workers:
  count: 4
  queueSize: 16
  tcpMaxBufferedPagesTotal: 4096
  tcpMaxBufferedPagesPerConn: 64
  udpMaxStreams: 4096
KujouRinka commented 7 months ago

Packet of No.30 is HelloClient with sni=www.bilibili.com. Before this packet was captured, curl command is blocking. But after it shows, connection established and data start to transfer. I did't filter udp packet, DNS query at begining is all udp packets I captured.

tobyxdd commented 7 months ago

Can you see if this commit fixes it for you? a2475d3

KujouRinka commented 7 months ago

It has been fixed! Thanks for your help!