def handle_syn_ack(syn_ack): uncertain_ip = socket.inet_ntoa(syn_ack.src) if uncertain_ip in pending_syn: del pending_syn[uncertain_ip] expected_ttl = syn_ack_ttl.get((uncertain_ip, syn_ack.tcp.sport)) or 0 if expected_ttl and abs(syn_ack.ttl - expected_ttl) > 2: record_jamming_event(uncertain_ip, 'tcp syn ack spoofing') LOGGER.error( 'received spoofed SYN ACK: expected ttl is %s, actually is %s, the packet %s' % (expected_ttl, syn_ack.ttl, format_ip_packet(syn_ack))) syn_ack_ttl[(uncertain_ip, syn_ack.tcp.sport)] = syn_ack.ttl # later one should be the correct one as GFW is closer to us if uncertain_ip in international_zone: inject_poison_ack_to_fill_gfw_buffer_with_garbage(syn_ack, international_zone[uncertain_ip]) return True elif uncertain_ip in domestic_zone: return True elif pending_connection.is_ip_pending(uncertain_ip): pending_connection.record_syn_ack(syn_ack) timeouted = pending_connection.is_ip_timeouted(uncertain_ip) if timeouted: international_ip = uncertain_ip LOGGER.info('treat ip as international due to timeout: %s' % international_ip) add_international_ip(international_ip, DEFAULT_TTL_TO_GFW) return False elif china_ip.is_china_ip(uncertain_ip): domestic_ip = uncertain_ip LOGGER.info('found domestic ip: %s' % domestic_ip) domestic_zone.add(domestic_ip) return True else: pending_connection.record_syn_ack(syn_ack) inject_ping_requests_to_find_right_ttl(uncertain_ip) return False
def handle_time_exceeded(ip_packet): time_exceed = ip_packet.icmp.data if not isinstance(time_exceed.data, dpkt.ip.IP): return te_ip_packet = time_exceed.data if not isinstance(te_ip_packet.data, dpkt.icmp.ICMP): return te_icmp_packet = te_ip_packet.data if not isinstance(te_icmp_packet.data, dpkt.icmp.ICMP.Echo): return te_icmp_echo = te_icmp_packet.data ttl = te_icmp_echo.id dst_ip = socket.inet_ntoa(te_ip_packet.dst) router_ip = socket.inet_ntoa(ip_packet.src) is_china_router = china_ip.is_china_ip(router_ip) if is_china_router and MAX_TTL_TO_GFW == ttl: LOGGER.info('treat ip as domestic as max ttl is still in china: %s' % dst_ip) add_domestic_ip(dst_ip) return elif not is_china_router and MIN_TTL_TO_GFW == ttl: LOGGER.info('treat ip as international as min ttl is not in china: %s' % dst_ip) add_international_ip(dst_ip, MAX_TTL_TO_GFW) return else: pending_connection.record_router(dst_ip, ttl, is_china_router) ttl_to_gfw = pending_connection.get_ttl_to_gfw(dst_ip) if ttl_to_gfw: LOGGER.info('found ttl to gfw: %s %s' % (dst_ip, ttl_to_gfw)) add_international_ip(dst_ip, ttl_to_gfw)
def handle_time_exceeded(ip_packet): time_exceed = ip_packet.icmp.data if not isinstance(time_exceed.data, dpkt.ip.IP): return te_ip_packet = time_exceed.data if not isinstance(te_ip_packet.data, dpkt.icmp.ICMP): return te_icmp_packet = te_ip_packet.data if not isinstance(te_icmp_packet.data, dpkt.icmp.ICMP.Echo): return te_icmp_echo = te_icmp_packet.data ttl = te_icmp_echo.id dst_ip = socket.inet_ntoa(te_ip_packet.dst) router_ip = socket.inet_ntoa(ip_packet.src) is_china_router = china_ip.is_china_ip(router_ip) if is_china_router and MAX_TTL_TO_GFW == ttl: LOGGER.info( 'treat ip as domestic as max ttl is still in china: %s, %s' % (dst_ip, pending_connection.get_detected_routers(dst_ip))) add_domestic_ip(dst_ip) return else: pending_connection.record_router(dst_ip, ttl, is_china_router) ttl_to_gfw = pending_connection.get_ttl_to_gfw(dst_ip) if ttl_to_gfw: LOGGER.info('found ttl to gfw: %s %s' % (dst_ip, ttl_to_gfw - SAFETY_DELTA)) add_international_ip(dst_ip, ttl_to_gfw - SAFETY_DELTA)
def pick_proxy_and_forward(client): if CHINA_PROXY and china_ip.is_china_ip(client.dst_ip): try: CHINA_PROXY.forward(client) except ProxyFallBack: pass return for i in range(3): proxy = pick_proxy(client) while proxy: if not client.host: break elif 'PUBLIC' in proxy.flags and any(fnmatch.fnmatch(client.host, host) for host in NO_PUBLIC_PROXY_HOSTS): client.tried_proxies.append(proxy) else: break proxy = pick_proxy(client) proxy = proxy or DIRECT_PROXY client.tried_proxies.append(proxy) if LOGGER.isEnabledFor(logging.DEBUG): LOGGER.debug('[%s] picked proxy: %s' % (repr(client), repr(proxy))) try: proxy.forward(client) return except ProxyFallBack, e: LOGGER.error('[%s] fall back to other proxy due to %s: %s' % (repr(client), e.reason, repr(proxy))) except NotHttp: continue
def handle_time_exceeded(ip_packet): global MAX_TTL_TO_GFW global MIN_TTL_TO_GFW global RANGE_OF_TTL_TO_GFW time_exceed = ip_packet.icmp.data if not isinstance(time_exceed.data, dpkt.ip.IP): return te_ip_packet = time_exceed.data if not isinstance(te_ip_packet.data, dpkt.icmp.ICMP): return te_icmp_packet = te_ip_packet.data if not isinstance(te_icmp_packet.data, dpkt.icmp.ICMP.Echo): return te_icmp_echo = te_icmp_packet.data ttl = te_icmp_echo.id dst_ip = socket.inet_ntoa(te_ip_packet.dst) router_ip = socket.inet_ntoa(ip_packet.src) is_china_router = china_ip.is_china_ip(router_ip) if is_china_router and MAX_TTL_TO_GFW == ttl: LOGGER.info('treat ip as domestic as max ttl is still in china: %s, %s' % (dst_ip, pending_connection.get_detected_routers(dst_ip))) add_domestic_ip(dst_ip) return else: pending_connection.record_router(dst_ip, ttl, is_china_router) ttl_to_gfw = pending_connection.get_ttl_to_gfw(dst_ip) if ttl_to_gfw: LOGGER.info('found ttl to gfw: %s %s' % (dst_ip, ttl_to_gfw - SAFETY_DELTA)) if ttl_to_gfw == MAX_TTL_TO_GFW: MIN_TTL_TO_GFW += 2 MAX_TTL_TO_GFW += 2 LOGGER.info('slide ttl range to [%s ~ %s]' % (MIN_TTL_TO_GFW, MAX_TTL_TO_GFW)) RANGE_OF_TTL_TO_GFW = range(MIN_TTL_TO_GFW, MAX_TTL_TO_GFW + 1) add_international_ip(dst_ip, ttl_to_gfw - SAFETY_DELTA)
def add_to_white_list(ip): if ip not in white_list and not china_ip.is_china_ip(ip): if ip in black_list: LOGGER.info('add white list ip from black list: %s' % ip) black_list.remove(ip) else: LOGGER.info('add white list ip: %s' % ip) white_list.add(ip) pending_list.pop(ip, None)
def handle_packet(nfqueue_element): try: ip_packet = dpkt.ip.IP(nfqueue_element.get_payload()) ip = socket.inet_ntoa(ip_packet.dst) if china_ip.is_china_ip(ip): nfqueue_element.accept() elif ip in white_list: nfqueue_element.accept() elif ip in black_list: set_verdict_proxy(nfqueue_element, ip_packet) else: nfqueue_element.accept() except: LOGGER.exception('failed to handle packet') nfqueue_element.accept()
def handle_syn_ack(syn_ack): uncertain_ip = socket.inet_ntoa(syn_ack.src) full_proxy_service.add_to_white_list(uncertain_ip) if uncertain_ip in pending_syn: del pending_syn[uncertain_ip] expected_ttl = syn_ack_ttl.get((uncertain_ip, syn_ack.tcp.sport)) or 0 if expected_ttl and abs(syn_ack.ttl - expected_ttl) > 2: log_jamming_event(uncertain_ip, 'tcp syn ack spoofing') LOGGER.error( 'received spoofed SYN ACK: expected ttl is %s, actually is %s, the packet %s' % (expected_ttl, syn_ack.ttl, format_ip_packet(syn_ack))) syn_ack_ttl[( uncertain_ip, syn_ack.tcp.sport )] = syn_ack.ttl # later one should be the correct one as GFW is closer to us if uncertain_ip in international_zone: inject_poison_ack_to_fill_gfw_buffer_with_garbage( syn_ack, international_zone[uncertain_ip]) return True elif uncertain_ip in domestic_zone: return True elif pending_connection.is_ip_pending(uncertain_ip): pending_connection.record_syn_ack(syn_ack) timeouted = pending_connection.is_ip_timeouted(uncertain_ip) if timeouted: international_ip = uncertain_ip LOGGER.info( 'treat ip as international due to timeout: %s, %s' % (international_ip, pending_connection.get_detected_routers(international_ip))) ttl_to_gfw = pending_connection.get_ttl_to_gfw( international_ip, exact_match_only=False) add_international_ip(international_ip, (ttl_to_gfw or DEFAULT_TTL_TO_GFW) - SAFETY_DELTA) return False elif china_ip.is_china_ip(uncertain_ip): domestic_ip = uncertain_ip LOGGER.info('found domestic ip: %s' % domestic_ip) domestic_zone.add(domestic_ip) return True else: pending_connection.record_syn_ack(syn_ack) inject_ping_requests_to_find_right_ttl(uncertain_ip) return False
def pick_proxy_and_forward(client): if lan_ip.is_lan_traffic(client.src_ip, client.dst_ip): try: DIRECT_PROXY.forward(client) except ProxyFallBack: pass return if client.dst_ip in fqdns.BUILTIN_WRONG_ANSWERS(): LOGGER.error('[%s] destination is GFW wrong answer' % repr(client)) NONE_PROXY.forward(client) return if CHINA_PROXY and china_ip.is_china_ip(client.dst_ip): try: CHINA_PROXY.forward(client) except ProxyFallBack: pass return for i in range(3): proxy = pick_proxy(client) while proxy: if not client.host: break elif 'PUBLIC' in proxy.flags and any(fnmatch.fnmatch(client.host, host) for host in NO_PUBLIC_PROXY_HOSTS): client.tried_proxies[proxy] = 'skip PUBLIC' else: break proxy = pick_proxy(client) proxy = proxy or DIRECT_PROXY if is_direct_access_disabled() and isinstance(proxy, DirectProxy): # try disabled LOGGER.info('[%s] no proxy available, and DIRECT has been disabled' % repr(client)) return if 'DIRECT' in proxy.flags: LOGGER.debug('[%s] picked proxy: %s' % (repr(client), repr(proxy))) else: LOGGER.info('[%s] picked proxy: %s' % (repr(client), repr(proxy))) try: proxy.forward(client) return except ProxyFallBack, e: LOGGER.error('[%s] fall back to other proxy due to %s: %s' % (repr(client), e.reason, repr(proxy))) client.tried_proxies[proxy] = e.reason except NotHttp: client.tried_proxies[proxy] = 'not http' continue
def handle_packet(nfqueue_element): try: if 0xcafe == nfqueue_element.get_mark(): nfqueue_element.accept() return ip_packet = dpkt.ip.IP(nfqueue_element.get_payload()) if ip_packet.tcp.dport not in {80, 443}: nfqueue_element.accept() return if lan_ip.is_lan_traffic(ip_packet): nfqueue_element.accept() return ip = socket.inet_ntoa(ip_packet.dst) if china_ip.is_china_ip(ip): nfqueue_element.accept() elif ip in black_list: set_verdict_proxy(nfqueue_element, ip_packet) elif ip in white_list: nfqueue_element.accept() else: nfqueue_element.accept() except: LOGGER.exception('failed to handle packet') nfqueue_element.accept()
def match_dst(self, src, dst): matched = china_ip.is_china_ip(dst) if matched: LOGGER.info(self.matched_dst % dst) return matched
def match_src(self, src, dst): matched = china_ip.is_china_ip(src) if matched: LOGGER.debug(self.matched_src % src) return matched
def match_dst(self, src, dst): matched = china_ip.is_china_ip(dst) if matched: LOGGER.debug(self.matched_dst % dst) return matched
def add_to_white_list(ip): if ip not in white_list and not china_ip.is_china_ip(ip): LOGGER.info('add white list ip: %s' % ip) white_list.add(ip) pending_list.pop(ip, None)
def match_src(self, src, dst): matched = china_ip.is_china_ip(src) if matched: LOGGER.info(self.matched_src % src) return matched